logstash-filter-jdbc_static 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +2 -0
- data/CONTRIBUTORS +22 -0
- data/Gemfile +2 -0
- data/LICENSE +13 -0
- data/README.md +94 -0
- data/lib/logstash-filter-jdbc_static_jars.rb +5 -0
- data/lib/logstash/filters/jdbc/basic_database.rb +117 -0
- data/lib/logstash/filters/jdbc/column.rb +38 -0
- data/lib/logstash/filters/jdbc/db_object.rb +103 -0
- data/lib/logstash/filters/jdbc/loader.rb +114 -0
- data/lib/logstash/filters/jdbc/loader_schedule.rb +38 -0
- data/lib/logstash/filters/jdbc/lookup.rb +192 -0
- data/lib/logstash/filters/jdbc/lookup_processor.rb +91 -0
- data/lib/logstash/filters/jdbc/lookup_result.rb +39 -0
- data/lib/logstash/filters/jdbc/read_only_database.rb +57 -0
- data/lib/logstash/filters/jdbc/read_write_database.rb +86 -0
- data/lib/logstash/filters/jdbc/repeating_load_runner.rb +11 -0
- data/lib/logstash/filters/jdbc/single_load_runner.rb +43 -0
- data/lib/logstash/filters/jdbc/validatable.rb +49 -0
- data/lib/logstash/filters/jdbc_static.rb +216 -0
- data/logstash-filter-jdbc_static.gemspec +38 -0
- data/spec/filters/env_helper.rb +10 -0
- data/spec/filters/jdbc/column_spec.rb +70 -0
- data/spec/filters/jdbc/db_object_spec.rb +81 -0
- data/spec/filters/jdbc/loader_spec.rb +76 -0
- data/spec/filters/jdbc/lookup_processor_spec.rb +132 -0
- data/spec/filters/jdbc/lookup_spec.rb +129 -0
- data/spec/filters/jdbc/read_only_database_spec.rb +66 -0
- data/spec/filters/jdbc/read_write_database_spec.rb +89 -0
- data/spec/filters/jdbc/repeating_load_runner_spec.rb +24 -0
- data/spec/filters/jdbc/single_load_runner_spec.rb +16 -0
- data/spec/filters/jdbc_static_file_local_spec.rb +83 -0
- data/spec/filters/jdbc_static_spec.rb +70 -0
- data/spec/filters/remote_server_helper.rb +24 -0
- data/spec/filters/shared_helpers.rb +35 -0
- data/spec/helpers/WHY-THIS-JAR.txt +4 -0
- data/spec/helpers/derbyrun.jar +0 -0
- data/vendor/jar-dependencies/runtime-jars/derby-10.14.1.0.jar +0 -0
- data/vendor/jar-dependencies/runtime-jars/derbyclient-10.14.1.0.jar +0 -0
- metadata +224 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative "single_load_runner"
|
2
|
+
|
3
|
+
module LogStash module Filters module Jdbc
|
4
|
+
class RepeatingLoadRunner < SingleLoadRunner
|
5
|
+
# info - attr_reader :local, :loaders, :preloaders
|
6
|
+
|
7
|
+
def repeated_load
|
8
|
+
local.repopulate_all(loaders)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end end end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative 'db_object'
|
2
|
+
|
3
|
+
module LogStash module Filters module Jdbc
|
4
|
+
class SingleLoadRunner
|
5
|
+
|
6
|
+
attr_reader :local, :loaders, :preloaders
|
7
|
+
|
8
|
+
def initialize(local, loaders, preloaders)
|
9
|
+
@local = local
|
10
|
+
@loaders = loaders
|
11
|
+
@preloaders = []
|
12
|
+
preloaders.map do |pre|
|
13
|
+
dbo = DbObject.new(pre)
|
14
|
+
@preloaders << dbo
|
15
|
+
hash = dbo.as_temp_table_opts
|
16
|
+
_dbo = DbObject.new(hash)
|
17
|
+
@preloaders << _dbo if _dbo.valid?
|
18
|
+
end
|
19
|
+
@preloaders.sort!
|
20
|
+
end
|
21
|
+
|
22
|
+
def initial_load
|
23
|
+
do_preload
|
24
|
+
local.populate_all(loaders)
|
25
|
+
end
|
26
|
+
|
27
|
+
def repeated_load
|
28
|
+
end
|
29
|
+
|
30
|
+
def call
|
31
|
+
repeated_load
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def do_preload
|
37
|
+
preloaders.each do |db_object|
|
38
|
+
local.build_db_object(db_object)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end end end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module LogStash module Filters module Jdbc
|
4
|
+
class Validatable
|
5
|
+
def self.find_validation_errors(array_of_options)
|
6
|
+
if !array_of_options.is_a?(Array)
|
7
|
+
return "The options must be an Array"
|
8
|
+
end
|
9
|
+
errors = []
|
10
|
+
array_of_options.each do |options|
|
11
|
+
instance = new(options)
|
12
|
+
unless instance.valid?
|
13
|
+
errors << instance.formatted_errors
|
14
|
+
end
|
15
|
+
end
|
16
|
+
return nil if errors.empty?
|
17
|
+
errors.join("; ")
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(options)
|
21
|
+
pre_initialize(options)
|
22
|
+
@options = options
|
23
|
+
@valid = false
|
24
|
+
@option_errors = []
|
25
|
+
parse_options
|
26
|
+
post_initialize
|
27
|
+
end
|
28
|
+
|
29
|
+
def valid?
|
30
|
+
@valid
|
31
|
+
end
|
32
|
+
|
33
|
+
def formatted_errors
|
34
|
+
@option_errors.join(", ")
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def pre_initialize(options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def post_initialize
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_options
|
46
|
+
raise "Subclass must implement 'parse_options'"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end end end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash-filter-jdbc_static_jars"
|
3
|
+
require "logstash/filters/base"
|
4
|
+
require "logstash/namespace"
|
5
|
+
require_relative "jdbc/loader"
|
6
|
+
require_relative "jdbc/loader_schedule"
|
7
|
+
require_relative "jdbc/repeating_load_runner"
|
8
|
+
require_relative "jdbc/lookup_processor"
|
9
|
+
|
10
|
+
# This filter can do multiple enhancements to an event in one pass.
|
11
|
+
# Define multiple loader sources and multiple lookup targets.
|
12
|
+
# Currently only one remote database connection is supported.
|
13
|
+
# [source,ruby]
|
14
|
+
|
15
|
+
#
|
16
|
+
module LogStash module Filters class JdbcStatic < LogStash::Filters::Base
|
17
|
+
config_name "jdbc_static"
|
18
|
+
|
19
|
+
# Define the loaders, an Array of Hashes, to fetch remote data and create local tables.
|
20
|
+
# the fetched data will be inserted into the local tables. Make sure that the
|
21
|
+
# local table name, columns and datatypes correspond to the shape of the remote data
|
22
|
+
# being fetched. The default for max_rows is 1 million rows. You may provide an `id`
|
23
|
+
# For example:
|
24
|
+
# loaders => [
|
25
|
+
# {
|
26
|
+
# id => "country_details"
|
27
|
+
# query => "select code, name from WORLD.COUNTRY"
|
28
|
+
# max_rows => 2000
|
29
|
+
# local_table => "country"
|
30
|
+
# },
|
31
|
+
# {
|
32
|
+
# id => "servers_load"
|
33
|
+
# query => "select id, ip, name, location from INTERNAL.SERVERS"
|
34
|
+
# local_table => "servers"
|
35
|
+
# }
|
36
|
+
# ]
|
37
|
+
# This is optional. You can provide a pre-populated local database server then no initial loaders are needed.
|
38
|
+
config :loaders, :required => false, :default => [], :validate => [LogStash::Filters::Jdbc::Loader]
|
39
|
+
|
40
|
+
# Define an array of Database Objects to create when the plugin first starts.
|
41
|
+
# These will usually be the definitions to setup the local in-memory tables.
|
42
|
+
# For example:
|
43
|
+
# local_db_objects => [
|
44
|
+
# {name => "servers", preserve_existing => true, index_columns => ["ip"], columns => [["id", "INTEGER"], ["ip", "varchar(64)"], ["name", "varchar(64)"], ["location", "varchar(64)"]]},
|
45
|
+
# ]
|
46
|
+
# NOTE: Important! use `preserve_existing => true` to keep a table created and filled in a previous Logstash session. It will default to false and is unneeded if the database is not persistent.
|
47
|
+
# NOTE: Important! Tables created here must have the same names as those used in the `loaders` and
|
48
|
+
# `local_lookups` configuration options
|
49
|
+
config :local_db_objects, :required => false, :default => [], :validate => [LogStash::Filters::Jdbc::DbObject]
|
50
|
+
|
51
|
+
# Define the list (Array) of enhancement local_lookups to be applied to an event
|
52
|
+
# Each entry is a hash of the query string, the target field and value and a
|
53
|
+
# parameter hash. Target is overwritten if existing. Target is optional,
|
54
|
+
# if omitted the lookup results will be written to the root of the event like this:
|
55
|
+
# event.set(<column name (or alias)>, <column value>)
|
56
|
+
# Use parameters to have this plugin put values from the event into the query.
|
57
|
+
# The parameter maps the symbol used in the query string to the field name in the event.
|
58
|
+
# NOTE: when using a query string that includes the LIKE keyword make sure that
|
59
|
+
# you provide a Logstash Event sprintf pattern with added wildcards.
|
60
|
+
# For example:
|
61
|
+
# local_lookups => [
|
62
|
+
# {
|
63
|
+
# "query" => "select * from country WHERE code = :code",
|
64
|
+
# "parameters" => {"code" => "country_code"}
|
65
|
+
# "target" => "country_details"
|
66
|
+
# },
|
67
|
+
# {
|
68
|
+
# "query" => "select ip, name from servers WHERE ip LIKE :ip",
|
69
|
+
# "parameters" => {"ip" => "%{[response][ip]}%"}
|
70
|
+
# "target" => "servers"
|
71
|
+
# }
|
72
|
+
# ]
|
73
|
+
config :local_lookups, :required => true, :validate => [LogStash::Filters::Jdbc::LookupProcessor]
|
74
|
+
|
75
|
+
# Schedule of when to periodically run loaders, in Cron format
|
76
|
+
# for example: "* * * * *" (execute query every minute, on the minute)
|
77
|
+
#
|
78
|
+
# There is no schedule by default. If no schedule is given, then the loaders are run
|
79
|
+
# exactly once.
|
80
|
+
config :loader_schedule, :validate => [LogStash::Filters::Jdbc::LoaderSchedule]
|
81
|
+
|
82
|
+
# Append values to the `tags` field if sql error occured
|
83
|
+
# Alternatively, individual `tag_on_failure` arrays can be added to each lookup hash
|
84
|
+
config :tag_on_failure, :validate => :array, :default => ["_jdbcstaticfailure"]
|
85
|
+
|
86
|
+
# Append values to the `tags` field if no record was found and default values were used
|
87
|
+
config :tag_on_default_use, :validate => :array, :default => ["_jdbcstaticdefaultsused"]
|
88
|
+
|
89
|
+
# Remote Load DB Jdbc driver library path to third party driver library.
|
90
|
+
config :jdbc_driver_library, :validate => :path
|
91
|
+
|
92
|
+
# Remote Load DB Jdbc driver class to load, for example "oracle.jdbc.OracleDriver" or "org.apache.derby.jdbc.ClientDriver"
|
93
|
+
config :jdbc_driver_class, :validate => :string, :required => true
|
94
|
+
|
95
|
+
# Remote Load DB Jdbc connection string
|
96
|
+
config :jdbc_connection_string, :validate => :string, :required => true
|
97
|
+
|
98
|
+
# Remote Load DB Jdbc user
|
99
|
+
config :jdbc_user, :validate => :string
|
100
|
+
|
101
|
+
# Remote Load DB Jdbc password
|
102
|
+
config :jdbc_password, :validate => :password
|
103
|
+
|
104
|
+
# NOTE: For the initial release, we are not allowing the user to specify their own local lookup JDBC DB settings.
|
105
|
+
# In the near future we have to consider identical config running in multiple pipelines stomping over each other
|
106
|
+
# when the database names are common across configs because there is only one Derby server in memory per JVM.
|
107
|
+
|
108
|
+
# Local Lookup DB Jdbc driver class to load, for example "org.apache.derby.jdbc.ClientDriver"
|
109
|
+
# config :lookup_jdbc_driver_class, :validate => :string, :required => false
|
110
|
+
|
111
|
+
# Local Lookup DB Jdbc driver library path to third party driver library.
|
112
|
+
# config :lookup_jdbc_driver_library, :validate => :path, :required => false
|
113
|
+
|
114
|
+
# Local Lookup DB Jdbc connection string
|
115
|
+
# config :lookup_jdbc_connection_string, :validate => :string, :required => false
|
116
|
+
|
117
|
+
class << self
|
118
|
+
alias_method :old_validate_value, :validate_value
|
119
|
+
|
120
|
+
def validate_value(value, validator)
|
121
|
+
if validator.is_a?(Array) && validator.first.respond_to?(:find_validation_errors)
|
122
|
+
validation_errors = validator.first.find_validation_errors(value)
|
123
|
+
unless validation_errors.nil?
|
124
|
+
return false, validation_errors
|
125
|
+
end
|
126
|
+
else
|
127
|
+
return old_validate_value(value, validator)
|
128
|
+
end
|
129
|
+
[true, value]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
public
|
134
|
+
|
135
|
+
def register
|
136
|
+
prepare_data_dir
|
137
|
+
prepare_runner
|
138
|
+
@loader_runner.initial_load
|
139
|
+
end
|
140
|
+
|
141
|
+
def filter(event)
|
142
|
+
enhancement_states = @processor.enhance(event)
|
143
|
+
filter_matched(event) if enhancement_states.all?
|
144
|
+
end
|
145
|
+
|
146
|
+
def stop
|
147
|
+
@scheduler.stop if @scheduler
|
148
|
+
@parsed_loaders.each(&:close)
|
149
|
+
@processor.close
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def prepare_data_dir
|
155
|
+
# later, when local persistent databases are allowed set this property to LS_HOME/data/jdbc-static/
|
156
|
+
# must take multi-pipelines into account and more than one config using the same jdbc-static settings
|
157
|
+
java.lang.System.setProperty("derby.system.home", ENV["HOME"])
|
158
|
+
end
|
159
|
+
|
160
|
+
def prepare_runner
|
161
|
+
@parsed_loaders = @loaders.map do |options|
|
162
|
+
add_plugin_configs(options)
|
163
|
+
loader = Jdbc::Loader.new(options)
|
164
|
+
loader.build_remote_db
|
165
|
+
loader
|
166
|
+
end
|
167
|
+
runner_args = [@parsed_loaders, @local_db_objects]
|
168
|
+
@processor = Jdbc::LookupProcessor.new(@local_lookups, global_lookup_options)
|
169
|
+
runner_args.unshift(@processor.local)
|
170
|
+
if @loader_schedule
|
171
|
+
require "rufus/scheduler"
|
172
|
+
args = []
|
173
|
+
@loader_runner = Jdbc::RepeatingLoadRunner.new(*runner_args)
|
174
|
+
|
175
|
+
cronline = Jdbc::LoaderSchedule.new(@loader_schedule)
|
176
|
+
rufus_args = {:max_work_threads => 1, :frequency => cronline.schedule_frequency}
|
177
|
+
|
178
|
+
@scheduler = Rufus::Scheduler.new(rufus_args)
|
179
|
+
@scheduler.cron(cronline.loader_schedule, @loader_runner)
|
180
|
+
@scheduler.join
|
181
|
+
else
|
182
|
+
@loader_runner = Jdbc::SingleLoadRunner.new(*runner_args)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def global_lookup_options(options = Hash.new)
|
187
|
+
if @tag_on_failure && !@tag_on_failure.empty? && !options.key?("tag_on_failure")
|
188
|
+
options["tag_on_failure"] = @tag_on_failure
|
189
|
+
end
|
190
|
+
if @tag_on_default_use && !@tag_on_default_use.empty? && !options.key?("tag_on_default_use")
|
191
|
+
options["tag_on_default_use"] = @tag_on_default_use
|
192
|
+
end
|
193
|
+
options["lookup_jdbc_driver_class"] = @lookup_jdbc_driver_class
|
194
|
+
options["lookup_jdbc_driver_library"] = @lookup_jdbc_driver_library
|
195
|
+
options["lookup_jdbc_connection_string"] = @lookup_jdbc_connection_string
|
196
|
+
options
|
197
|
+
end
|
198
|
+
|
199
|
+
def add_plugin_configs(options)
|
200
|
+
if @jdbc_driver_library
|
201
|
+
options["jdbc_driver_library"] = @jdbc_driver_library
|
202
|
+
end
|
203
|
+
if @jdbc_driver_class
|
204
|
+
options["jdbc_driver_class"] = @jdbc_driver_class
|
205
|
+
end
|
206
|
+
if @jdbc_connection_string
|
207
|
+
options["jdbc_connection_string"] = @jdbc_connection_string
|
208
|
+
end
|
209
|
+
if @jdbc_user
|
210
|
+
options["jdbc_user"] = @jdbc_user
|
211
|
+
end
|
212
|
+
if @jdbc_password
|
213
|
+
options["jdbc_password"] = @jdbc_password
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end end end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'logstash-filter-jdbc_static'
|
3
|
+
s.version = '1.0.0'
|
4
|
+
s.licenses = ['Apache License (2.0)']
|
5
|
+
s.summary = "This filter executes a SQL query to fetch a SQL query result, store it locally then use a second SQL query to update an event."
|
6
|
+
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"
|
7
|
+
s.authors = ["Elastic"]
|
8
|
+
s.email = 'info@elastic.co'
|
9
|
+
s.homepage = "http://www.elastic.co/guide/en/logstash/current/index.html"
|
10
|
+
# to fool jar_dependencies to mimic our other plugin gradle vendor script behaviour
|
11
|
+
# the rake vendor task removes jars dir and jars downloaded to it
|
12
|
+
s.require_paths = ["lib", "jars"]
|
13
|
+
|
14
|
+
# Files
|
15
|
+
s.files = Dir['lib/**/*','vendor/**/*','spec/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
|
16
|
+
# Tests
|
17
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
18
|
+
|
19
|
+
# Special flag to let us know this is actually a logstash plugin
|
20
|
+
s.metadata = { "logstash_plugin" => "true", "logstash_group" => "filter" }
|
21
|
+
|
22
|
+
derby_version = "10.14.1.0"
|
23
|
+
s.requirements << "jar 'org.apache.derby:derby', '#{derby_version}'"
|
24
|
+
s.requirements << "jar 'org.apache.derby:derbyclient', '#{derby_version}'"
|
25
|
+
# we may need 'org.apache.derby:derbynet' in the future, marking this here
|
26
|
+
|
27
|
+
s.add_development_dependency 'jar-dependencies', '~> 0.3'
|
28
|
+
|
29
|
+
# Gem dependencies
|
30
|
+
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
|
31
|
+
s.add_runtime_dependency 'sequel'
|
32
|
+
s.add_runtime_dependency 'tzinfo'
|
33
|
+
s.add_runtime_dependency 'tzinfo-data'
|
34
|
+
s.add_runtime_dependency 'rufus-scheduler'
|
35
|
+
|
36
|
+
s.add_development_dependency 'logstash-devutils'
|
37
|
+
s.add_development_dependency "childprocess"
|
38
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# use the rspec --require command line option to have this file evaluated before rspec runs
|
4
|
+
# it i
|
5
|
+
|
6
|
+
GEM_BASE_DIR = ::File.expand_path("../../..", __FILE__)
|
7
|
+
BASE_DERBY_DIR = ::File.join(GEM_BASE_DIR, "spec", "helpers")
|
8
|
+
ENV["HOME"] = GEM_BASE_DIR
|
9
|
+
ENV["TEST_DEBUG"] = "true"
|
10
|
+
java.lang.System.setProperty("ls.logs", "console")
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/devutils/rspec/spec_helper"
|
3
|
+
require "logstash/filters/jdbc/column"
|
4
|
+
|
5
|
+
describe LogStash::Filters::Jdbc::Column do
|
6
|
+
let(:invalid_messages) do
|
7
|
+
[
|
8
|
+
"The column options must be an array",
|
9
|
+
"The first column option is the name and must be a string",
|
10
|
+
"The second column option is the datatype and must be a string"
|
11
|
+
]
|
12
|
+
end
|
13
|
+
|
14
|
+
context "various invalid non-array arguments" do
|
15
|
+
it "a nil does not validate" do
|
16
|
+
instance = described_class.new(nil)
|
17
|
+
expect(instance.valid?).to be_falsey
|
18
|
+
expect(instance.formatted_errors).to eq(invalid_messages.join(", "))
|
19
|
+
end
|
20
|
+
|
21
|
+
it "a string does not validate" do
|
22
|
+
instance = described_class.new("foo")
|
23
|
+
expect(instance.valid?).to be_falsey
|
24
|
+
expect(instance.formatted_errors).to eq(invalid_messages.values_at(0,2).join(", "))
|
25
|
+
end
|
26
|
+
|
27
|
+
it "a number does not validate" do
|
28
|
+
instance = described_class.new(42)
|
29
|
+
expect(instance.valid?).to be_falsey
|
30
|
+
expect(instance.formatted_errors).to eq(invalid_messages.join(", "))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "various invalid array arguments" do
|
35
|
+
it "a single string element does not validate" do
|
36
|
+
instance = described_class.new(["foo"])
|
37
|
+
expect(instance.valid?).to be_falsey
|
38
|
+
expect(instance.formatted_errors).to eq(invalid_messages.last)
|
39
|
+
end
|
40
|
+
[ [], [1, 2] ].each do |arg|
|
41
|
+
it "do not validate" do
|
42
|
+
instance = described_class.new(arg)
|
43
|
+
expect(instance.valid?).to be_falsey
|
44
|
+
expect(instance.formatted_errors).to eq(invalid_messages.values_at(1,2).join(", "))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
[ ["foo", 3], ["foo", nil] ].each do |arg|
|
48
|
+
it "do not validate" do
|
49
|
+
instance = described_class.new(arg)
|
50
|
+
expect(instance.valid?).to be_falsey
|
51
|
+
expect(instance.formatted_errors).to eq(invalid_messages.last)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
[ [3, "foo"], [nil, "foo"] ].each do |arg|
|
55
|
+
it "do not validate" do
|
56
|
+
instance = described_class.new(arg)
|
57
|
+
expect(instance.valid?).to be_falsey
|
58
|
+
expect(instance.formatted_errors).to eq(invalid_messages[1])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "a valid array argument" do
|
64
|
+
it "does validate" do
|
65
|
+
instance = described_class.new(["foo", "varchar2"])
|
66
|
+
expect(instance.valid?).to be_truthy
|
67
|
+
expect(instance.formatted_errors).to eq("")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/devutils/rspec/spec_helper"
|
3
|
+
require "logstash/filters/jdbc/db_object"
|
4
|
+
|
5
|
+
describe LogStash::Filters::Jdbc::DbObject do
|
6
|
+
context "various invalid non-hash arguments" do
|
7
|
+
it "a nil does not validate" do
|
8
|
+
instance = described_class.new(nil)
|
9
|
+
expect(instance.valid?).to be_falsey
|
10
|
+
expect(instance.formatted_errors).to eq("DbObject options must be a Hash")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "a string does not validate" do
|
14
|
+
instance = described_class.new("foo")
|
15
|
+
expect(instance.valid?).to be_falsey
|
16
|
+
expect(instance.formatted_errors).to eq("DbObject options must be a Hash")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "a number does not validate" do
|
20
|
+
instance = described_class.new(42)
|
21
|
+
expect(instance.valid?).to be_falsey
|
22
|
+
expect(instance.formatted_errors).to eq("DbObject options must be a Hash")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "various invalid hash arguments" do
|
27
|
+
let(:error_messages) do
|
28
|
+
[
|
29
|
+
"DbObject options must include a 'name' string",
|
30
|
+
"DbObject options for 'foo' must include a 'columns' array"
|
31
|
+
]
|
32
|
+
end
|
33
|
+
"DbObject options must include a 'name' string, DbObject options for 'unnamed' must include a 'columns' array"
|
34
|
+
it "an empty hash does not validate" do
|
35
|
+
instance = described_class.new({})
|
36
|
+
expect(instance.valid?).to be_falsey
|
37
|
+
expect(instance.formatted_errors).to eq(error_messages.values_at(0,1).join(", ").gsub('foo', 'unnamed'))
|
38
|
+
end
|
39
|
+
|
40
|
+
it "a name key value only" do
|
41
|
+
instance = described_class.new({"name" => "foo"})
|
42
|
+
expect(instance.valid?).to be_falsey
|
43
|
+
expect(instance.formatted_errors).to eq(error_messages[1])
|
44
|
+
end
|
45
|
+
|
46
|
+
it "a name and bad columns" do
|
47
|
+
instance = described_class.new({"name" => "foo", "columns" => 42})
|
48
|
+
expect(instance.valid?).to be_falsey
|
49
|
+
expect(instance.formatted_errors).to eq(error_messages[1])
|
50
|
+
end
|
51
|
+
|
52
|
+
it "a name and bad columns - empty array" do
|
53
|
+
instance = described_class.new({"name" => "foo", "columns" => []})
|
54
|
+
expect(instance.valid?).to be_falsey
|
55
|
+
msg = "The columns array for 'foo' is not uniform, it should contain arrays of two strings only"
|
56
|
+
expect(instance.formatted_errors).to eq(msg)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "a name and bad columns - irregular arrays" do
|
60
|
+
instance = described_class.new({"name" => "foo", "columns" => [["ip", "text"], ["name"], ["a", "b", "c"]]})
|
61
|
+
expect(instance.valid?).to be_falsey
|
62
|
+
msg = "The columns array for 'foo' is not uniform, it should contain arrays of two strings only"
|
63
|
+
expect(instance.formatted_errors).to eq(msg)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "a name, good columns and bad index_column" do
|
67
|
+
instance = described_class.new({"name" => "foo_index", "index_columns" => ["bar"], "columns" => [["ip", "text"], ["name", "text"]]})
|
68
|
+
expect(instance.valid?).to be_falsey
|
69
|
+
msg = "The index_columns element: 'bar' must be a column defined in the columns array"
|
70
|
+
expect(instance.formatted_errors).to eq(msg)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "a valid hash argument" do
|
75
|
+
it "does validate" do
|
76
|
+
instance = described_class.new({"name" => "foo", "index_columns" => ["ip"], "columns" => [["ip", "text"], ["name", "text"]]})
|
77
|
+
expect(instance.formatted_errors).to eq("")
|
78
|
+
expect(instance.valid?).to be_truthy
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|