connectors_service 8.6.0.4.pre.20221104T200814Z → 8.6.0.4.pre.20221114T233727Z
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 +4 -4
- data/config/connectors.yml +6 -6
- data/lib/app/dispatcher.rb +12 -0
- data/lib/app/preflight_check.rb +11 -0
- data/lib/connectors/base/connector.rb +19 -12
- data/lib/connectors/base/simple_rules_parser.rb +42 -0
- data/lib/connectors/example/connector.rb +15 -0
- data/lib/connectors/gitlab/connector.rb +15 -1
- data/lib/connectors/mongodb/connector.rb +55 -36
- data/lib/connectors/mongodb/mongo_rules_parser.rb +81 -0
- data/lib/core/configuration.rb +3 -1
- data/lib/core/connector_job.rb +137 -0
- data/lib/core/connector_settings.rb +24 -11
- data/lib/core/elastic_connector_actions.rb +263 -24
- data/lib/core/filtering/post_process_engine.rb +39 -0
- data/lib/core/filtering/post_process_result.rb +27 -0
- data/lib/core/filtering/simple_rule.rb +141 -0
- data/lib/core/filtering/validation_job_runner.rb +53 -0
- data/lib/core/filtering/validation_status.rb +17 -0
- data/lib/core/filtering.rb +17 -0
- data/lib/core/ingestion/es_sink.rb +59 -0
- data/lib/core/ingestion/ingester.rb +90 -0
- data/lib/core/{output_sink.rb → ingestion.rb} +2 -5
- data/lib/core/scheduler.rb +40 -10
- data/lib/core/sync_job_runner.rb +65 -17
- data/lib/core.rb +2 -0
- data/lib/utility/bulk_queue.rb +85 -0
- data/lib/utility/constants.rb +2 -0
- data/lib/utility/filtering.rb +22 -0
- data/lib/utility/logger.rb +2 -1
- data/lib/utility.rb +5 -4
- metadata +16 -7
- data/lib/core/output_sink/base_sink.rb +0 -33
- data/lib/core/output_sink/combined_sink.rb +0 -38
- data/lib/core/output_sink/console_sink.rb +0 -51
- data/lib/core/output_sink/es_sink.rb +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f69f05260d34b07ce34d569ce7c41fdd10349b33121823697ebbb6a4ebf9206
|
4
|
+
data.tar.gz: a2957118c80d0e2bc9ea6a8046307485c11e4f809efb01cabbb96e341dc947c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63775eded9d9953b41950edd7ca86176200c4ae7510564f6f7995c336d6e78bbe40d494cb0a152a984b14b45055e7b7847a2779888d70905d4956b8c78d4bda1
|
7
|
+
data.tar.gz: 52b00d122ef43fc5afa0b4cb50bbe428111e7fe2cb7cee437dd2d2b6b32516cbd01c5803b0a11609a5021e85605a2e6bd2b30973807df3cae1420864e2fcb185
|
data/config/connectors.yml
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# general metadata
|
2
|
-
version: 8.6.0.4-
|
2
|
+
version: 8.6.0.4-20221114T233727Z
|
3
3
|
repository: git@github.com:elastic/ent-search-connectors.git
|
4
|
-
revision:
|
4
|
+
revision: f506d5e5ebedfb0c6058d347d8ce22adc42e2cc0
|
5
5
|
elasticsearch:
|
6
6
|
hosts: http://localhost:9200
|
7
|
-
api_key:
|
7
|
+
api_key: WXNYeWQ0UUJ4Y3ZQV3ctbjVibnU6REx4eE8tbFhUMU94N2JoU2hIeVFMQQ==
|
8
8
|
retry_on_failure: 3
|
9
9
|
request_timeout: 120
|
10
10
|
disable_warnings: true
|
@@ -14,11 +14,11 @@ thread_pool:
|
|
14
14
|
min_threads: 0
|
15
15
|
max_threads: 5
|
16
16
|
max_queue: 100
|
17
|
-
log_level:
|
17
|
+
log_level: debug
|
18
18
|
ecs_logging: false
|
19
19
|
poll_interval: 3
|
20
20
|
termination_timeout: 60
|
21
21
|
heartbeat_interval: 1800
|
22
22
|
native_mode: false
|
23
|
-
connector_id:
|
24
|
-
service_type:
|
23
|
+
connector_id: YcXyd4QBxcvPWw-n2bkA
|
24
|
+
service_type: mongodb
|
data/lib/app/dispatcher.rb
CHANGED
@@ -73,6 +73,8 @@ module App
|
|
73
73
|
start_heartbeat_task(connector_settings)
|
74
74
|
when :configuration
|
75
75
|
start_configuration_task(connector_settings)
|
76
|
+
when :filter_validation
|
77
|
+
start_filter_validation_task(connector_settings)
|
76
78
|
else
|
77
79
|
Utility::Logger.error("Unknown task type: #{task}. Skipping...")
|
78
80
|
end
|
@@ -120,6 +122,16 @@ module App
|
|
120
122
|
Utility::ExceptionTracking.log_exception(e, "Configuration task for #{connector_settings.formatted} failed due to unexpected error.")
|
121
123
|
end
|
122
124
|
end
|
125
|
+
|
126
|
+
def start_filter_validation_task(connector_settings)
|
127
|
+
pool.post do
|
128
|
+
Utility::Logger.info("Validating filters for #{connector_settings.formatted}...")
|
129
|
+
validation_job_runner = Core::Filtering::ValidationJobRunner.new(connector_settings)
|
130
|
+
validation_job_runner.execute
|
131
|
+
rescue StandardError => e
|
132
|
+
Utility::ExceptionTracking.log_exception(e, "Filter validation task for #{connector_settings.formatted} failed due to unexpected error.")
|
133
|
+
end
|
134
|
+
end
|
123
135
|
end
|
124
136
|
end
|
125
137
|
end
|
data/lib/app/preflight_check.rb
CHANGED
@@ -23,6 +23,7 @@ module App
|
|
23
23
|
check_es_connection!
|
24
24
|
check_es_version!
|
25
25
|
check_system_indices!
|
26
|
+
check_single_connector!
|
26
27
|
end
|
27
28
|
|
28
29
|
private
|
@@ -59,6 +60,16 @@ module App
|
|
59
60
|
)
|
60
61
|
end
|
61
62
|
|
63
|
+
#-------------------------------------------------------------------------------------------------
|
64
|
+
# Ensures the connector is supported when running in non-native mode
|
65
|
+
def check_single_connector!
|
66
|
+
if App::Config.native_mode
|
67
|
+
Utility::Logger.info('Skip single connector check for native mode.')
|
68
|
+
elsif !Connectors::REGISTRY.registered?(App::Config.service_type)
|
69
|
+
fail_check!("The service type #{App::Config.service_type} is not supported. Terminating...")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
62
73
|
def check_es_connection_with_retries!(retry_interval:, retry_timeout:)
|
63
74
|
started_at = Time.now
|
64
75
|
|
@@ -7,8 +7,9 @@
|
|
7
7
|
# frozen_string_literal: true
|
8
8
|
|
9
9
|
require 'bson'
|
10
|
-
require 'core/
|
10
|
+
require 'core/ingestion'
|
11
11
|
require 'utility'
|
12
|
+
require 'utility/filtering'
|
12
13
|
require 'app/config'
|
13
14
|
require 'active_support/core_ext/hash/indifferent_access'
|
14
15
|
|
@@ -32,16 +33,27 @@ module Connectors
|
|
32
33
|
raise 'Not implemented for this connector'
|
33
34
|
end
|
34
35
|
|
36
|
+
def self.kibana_features
|
37
|
+
[
|
38
|
+
Utility::Constants::FILTERING_RULES_FEATURE,
|
39
|
+
Utility::Constants::FILTERING_ADVANCED_FEATURE
|
40
|
+
]
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.validate_filtering(_filtering = {})
|
44
|
+
raise 'Not implemented for this connector'
|
45
|
+
end
|
46
|
+
|
35
47
|
attr_reader :rules, :advanced_filter_config
|
36
48
|
|
37
49
|
def initialize(configuration: {}, job_description: {})
|
38
50
|
@configuration = configuration.dup || {}
|
39
51
|
@job_description = job_description&.dup || {}
|
40
52
|
|
41
|
-
|
53
|
+
filtering = Utility::Filtering.extract_filter(@job_description.dig(:connector, :filtering))
|
42
54
|
|
43
|
-
@rules =
|
44
|
-
@advanced_filter_config =
|
55
|
+
@rules = filtering[:rules] || []
|
56
|
+
@advanced_filter_config = filtering[:advanced_snippet] || {}
|
45
57
|
end
|
46
58
|
|
47
59
|
def yield_documents; end
|
@@ -67,16 +79,11 @@ module Connectors
|
|
67
79
|
end
|
68
80
|
|
69
81
|
def filtering_present?
|
70
|
-
@advanced_filter_config.present? || @rules.present?
|
82
|
+
@advanced_filter_config.present? && !@advanced_filter_config.empty? || @rules.present?
|
71
83
|
end
|
72
84
|
|
73
|
-
|
74
|
-
|
75
|
-
def get_filter(filtering)
|
76
|
-
# assume for now, that first object in filtering array or a filter object itself is the only filtering object
|
77
|
-
filter = filtering.is_a?(Array) ? filtering[0] : filtering
|
78
|
-
|
79
|
-
filter.present? ? filter : {}
|
85
|
+
def metadata
|
86
|
+
{}
|
80
87
|
end
|
81
88
|
end
|
82
89
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#
|
2
|
+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
3
|
+
# or more contributor license agreements. Licensed under the Elastic License;
|
4
|
+
# you may not use this file except in compliance with the Elastic License.
|
5
|
+
#
|
6
|
+
# frozen_string_literal: true
|
7
|
+
|
8
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
9
|
+
require 'active_support/core_ext/object/blank'
|
10
|
+
require 'core/filtering/simple_rule'
|
11
|
+
|
12
|
+
module Connectors
|
13
|
+
module Base
|
14
|
+
class SimpleRulesParser
|
15
|
+
def initialize(rules)
|
16
|
+
@rules = (rules || []).map(&:with_indifferent_access).filter { |r| r[:id] != 'DEFAULT' }.sort_by { |r| r[:order] }
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse
|
20
|
+
merge_rules(@rules.map do |rule_hash|
|
21
|
+
rule = Core::Filtering::SimpleRule.new(rule_hash)
|
22
|
+
unless rule.is_include? || rule.is_exclude?
|
23
|
+
raise "Unknown policy: #{rule.policy}"
|
24
|
+
end
|
25
|
+
parse_rule(rule)
|
26
|
+
end)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# merge all rules into a filter object or array
|
32
|
+
# in a base case, does no transformations
|
33
|
+
def merge_rules(rules)
|
34
|
+
rules || []
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_rule(_rule)
|
38
|
+
raise 'Not implemented'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -7,6 +7,7 @@
|
|
7
7
|
# frozen_string_literal: true
|
8
8
|
|
9
9
|
require 'connectors/base/connector'
|
10
|
+
require 'core/filtering/validation_status'
|
10
11
|
require 'utility'
|
11
12
|
|
12
13
|
module Connectors
|
@@ -45,6 +46,20 @@ module Connectors
|
|
45
46
|
# raise 'something went wrong'
|
46
47
|
end
|
47
48
|
|
49
|
+
def self.validate_filtering(filtering = {})
|
50
|
+
# TODO: real filtering validation will follow later
|
51
|
+
errors = [
|
52
|
+
{
|
53
|
+
:ids => ['missing-implementation'],
|
54
|
+
:messages => ['Filtering is not implemented yet for the example connector']
|
55
|
+
}
|
56
|
+
]
|
57
|
+
|
58
|
+
return { :state => Core::Filtering::ValidationStatus::INVALID, :errors => errors } if filtering.present?
|
59
|
+
|
60
|
+
{ :state => Core::Filtering::ValidationStatus::VALID, :errors => [] }
|
61
|
+
end
|
62
|
+
|
48
63
|
def yield_documents
|
49
64
|
attachments = [
|
50
65
|
load_attachment('first_attachment.txt'),
|
@@ -11,7 +11,7 @@ require 'connectors/base/connector'
|
|
11
11
|
require 'connectors/gitlab/extractor'
|
12
12
|
require 'connectors/gitlab/custom_client'
|
13
13
|
require 'connectors/gitlab/adapter'
|
14
|
-
require 'core/
|
14
|
+
require 'core/ingestion'
|
15
15
|
|
16
16
|
module Connectors
|
17
17
|
module GitLab
|
@@ -36,6 +36,20 @@ module Connectors
|
|
36
36
|
}
|
37
37
|
end
|
38
38
|
|
39
|
+
def self.validate_filtering(filtering = {})
|
40
|
+
# TODO: real filtering validation will follow later
|
41
|
+
errors = [
|
42
|
+
{
|
43
|
+
:ids => ['missing-implementation'],
|
44
|
+
:messages => ['Filtering is not implemented yet for the GitLab connector']
|
45
|
+
}
|
46
|
+
]
|
47
|
+
|
48
|
+
return { :state => Core::Filtering::ValidationStatus::INVALID, :errors => errors } if filtering.present?
|
49
|
+
|
50
|
+
{ :state => Core::Filtering::ValidationStatus::VALID, :errors => [] }
|
51
|
+
end
|
52
|
+
|
39
53
|
def initialize(configuration: {}, job_description: {})
|
40
54
|
super
|
41
55
|
|
@@ -7,6 +7,8 @@
|
|
7
7
|
# frozen_string_literal: true
|
8
8
|
|
9
9
|
require 'connectors/base/connector'
|
10
|
+
require 'core/filtering/validation_status'
|
11
|
+
require 'connectors/mongodb/mongo_rules_parser'
|
10
12
|
require 'mongo'
|
11
13
|
require 'utility'
|
12
14
|
|
@@ -28,27 +30,46 @@ module Connectors
|
|
28
30
|
|
29
31
|
def self.configurable_fields
|
30
32
|
{
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
33
|
+
:host => {
|
34
|
+
:label => 'Server Hostname'
|
35
|
+
},
|
36
|
+
:user => {
|
37
|
+
:label => 'Username'
|
38
|
+
},
|
39
|
+
:password => {
|
40
|
+
:label => 'Password'
|
41
|
+
},
|
42
|
+
:database => {
|
43
|
+
:label => 'Database'
|
44
|
+
},
|
45
|
+
:collection => {
|
46
|
+
:label => 'Collection'
|
47
|
+
},
|
48
|
+
:direct_connection => {
|
49
|
+
:label => 'Direct connection? (true/false)'
|
50
|
+
}
|
49
51
|
}
|
50
52
|
end
|
51
53
|
|
54
|
+
def self.validate_filtering(filtering = {})
|
55
|
+
valid_filtering = { :state => Core::Filtering::ValidationStatus::VALID, :errors => [] }
|
56
|
+
|
57
|
+
return valid_filtering unless filtering.present?
|
58
|
+
|
59
|
+
filter = Utility::Filtering.extract_filter(filtering)
|
60
|
+
|
61
|
+
advanced_filter_config = filter[:advanced_snippet] || {}
|
62
|
+
filter_keys = advanced_filter_config&.keys
|
63
|
+
|
64
|
+
if !filter_keys&.empty? && (filter_keys.size != 1 || !ALLOWED_TOP_LEVEL_FILTER_KEYS.include?(filter_keys[0]&.to_s))
|
65
|
+
return { :state => Core::Filtering::ValidationStatus::INVALID,
|
66
|
+
:errors => [{ :ids => ['wrong-keys'],
|
67
|
+
:messages => ["Only one of #{ALLOWED_TOP_LEVEL_FILTER_KEYS} is allowed in the filtering object. Keys present: '#{filter_keys}'."] }] }
|
68
|
+
end
|
69
|
+
|
70
|
+
valid_filtering
|
71
|
+
end
|
72
|
+
|
52
73
|
def initialize(configuration: {}, job_description: {})
|
53
74
|
super
|
54
75
|
|
@@ -61,8 +82,6 @@ module Connectors
|
|
61
82
|
end
|
62
83
|
|
63
84
|
def yield_documents
|
64
|
-
check_filtering
|
65
|
-
|
66
85
|
with_client do |client|
|
67
86
|
# We do paging using skip().limit() here to make Ruby recycle the memory for each page pulled from the server after it's not needed any more.
|
68
87
|
# This gives us more control on the usage of the memory (we can adjust PAGE_SIZE constant for that to decrease max memory consumption).
|
@@ -89,6 +108,7 @@ module Connectors
|
|
89
108
|
loop do
|
90
109
|
found_in_page = 0
|
91
110
|
|
111
|
+
Utility::Logger.info("Requesting #{PAGE_SIZE} documents from MongoDB (Starting at #{skip})")
|
92
112
|
view = cursor.skip(skip).limit(PAGE_SIZE)
|
93
113
|
view.each do |document|
|
94
114
|
yield serialize(document)
|
@@ -117,20 +137,9 @@ module Connectors
|
|
117
137
|
|
118
138
|
return create_aggregate_cursor(collection) if @advanced_filter_config[:aggregate].present?
|
119
139
|
|
120
|
-
collection.
|
121
|
-
end
|
122
|
-
|
123
|
-
def check_filtering
|
124
|
-
return unless filtering_present?
|
140
|
+
return create_simple_rules_cursor(collection) if @rules.present?
|
125
141
|
|
126
|
-
|
127
|
-
end
|
128
|
-
|
129
|
-
def check_find_and_aggregate
|
130
|
-
if @advanced_filter_config.keys.size != 1
|
131
|
-
invalid_keys_msg = "Only one of #{ALLOWED_TOP_LEVEL_FILTER_KEYS} is allowed in the filtering object. Keys present: '#{@advanced_filter_config.keys}'."
|
132
|
-
raise Utility::InvalidFilterConfigError.new(invalid_keys_msg)
|
133
|
-
end
|
142
|
+
collection.find
|
134
143
|
end
|
135
144
|
|
136
145
|
def create_aggregate_cursor(collection)
|
@@ -159,6 +168,16 @@ module Connectors
|
|
159
168
|
[collection.find(filter, options), options]
|
160
169
|
end
|
161
170
|
|
171
|
+
def create_simple_rules_cursor(collection)
|
172
|
+
filter = {}
|
173
|
+
if @rules.present?
|
174
|
+
parser = MongoRulesParser.new(@rules)
|
175
|
+
filter = parser.parse
|
176
|
+
end
|
177
|
+
Utility::Logger.info("Filtering with simple rules filter: #{filter}")
|
178
|
+
filter.present? ? collection.find(filter) : collection.find
|
179
|
+
end
|
180
|
+
|
162
181
|
def extract_options(mongodb_function)
|
163
182
|
mongodb_function[:options].present? ? mongodb_function[:options] : {}
|
164
183
|
end
|
@@ -173,9 +192,9 @@ module Connectors
|
|
173
192
|
raise "Invalid value for 'Direct connection' : #{@direct_connection}." unless %w[true false].include?(@direct_connection.to_s.strip.downcase)
|
174
193
|
|
175
194
|
args = {
|
176
|
-
|
177
|
-
|
178
|
-
|
195
|
+
database: @database,
|
196
|
+
direct_connection: to_boolean(@direct_connection)
|
197
|
+
}
|
179
198
|
|
180
199
|
if @user.present? || @password.present?
|
181
200
|
args[:user] = @user
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#
|
2
|
+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
3
|
+
# or more contributor license agreements. Licensed under the Elastic License;
|
4
|
+
# you may not use this file except in compliance with the Elastic License.
|
5
|
+
#
|
6
|
+
|
7
|
+
# frozen_string_literal: true
|
8
|
+
|
9
|
+
require 'active_support/core_ext/object'
|
10
|
+
require 'connectors/base/simple_rules_parser'
|
11
|
+
require 'core/filtering/simple_rule'
|
12
|
+
|
13
|
+
module Connectors
|
14
|
+
module MongoDB
|
15
|
+
class MongoRulesParser < Connectors::Base::SimpleRulesParser
|
16
|
+
def parse_rule(rule)
|
17
|
+
field = rule.field
|
18
|
+
value = rule.value
|
19
|
+
unless value.present?
|
20
|
+
raise "value is required for field: #{field}"
|
21
|
+
end
|
22
|
+
unless field.present?
|
23
|
+
raise "field is required for rule: #{rule}"
|
24
|
+
end
|
25
|
+
op = rule.rule
|
26
|
+
case op
|
27
|
+
when Core::Filtering::SimpleRule::Rule::EQUALS
|
28
|
+
parse_equals(rule)
|
29
|
+
when Core::Filtering::SimpleRule::Rule::GREATER_THAN
|
30
|
+
parse_greater_than(rule)
|
31
|
+
when Core::Filtering::SimpleRule::Rule::LESS_THAN
|
32
|
+
parse_less_than(rule)
|
33
|
+
when Core::Filtering::SimpleRule::Rule::REGEX
|
34
|
+
parse_regex(rule)
|
35
|
+
else
|
36
|
+
raise "Unknown operator: #{op}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def merge_rules(rules)
|
41
|
+
return {} if rules.empty?
|
42
|
+
return rules[0] if rules.size == 1
|
43
|
+
{ '$and' => rules }
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def parse_equals(rule)
|
49
|
+
if rule.is_include?
|
50
|
+
{ rule.field => rule.value }
|
51
|
+
else
|
52
|
+
{ rule.field => { '$ne' => rule.value } }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_greater_than(rule)
|
57
|
+
if rule.is_include?
|
58
|
+
{ rule.field => { '$gt' => rule.value } }
|
59
|
+
else
|
60
|
+
{ rule.field => { '$lte' => rule.value } }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_less_than(rule)
|
65
|
+
if rule.is_include?
|
66
|
+
{ rule.field => { '$lt' => rule.value } }
|
67
|
+
else
|
68
|
+
{ rule.field => { '$gte' => rule.value } }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_regex(rule)
|
73
|
+
if rule.is_include?
|
74
|
+
{ rule.field => /#{rule.value}/ }
|
75
|
+
else
|
76
|
+
{ rule.field => { '$not' => /#{rule.value}/ } }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/core/configuration.rb
CHANGED
@@ -24,8 +24,10 @@ module Core
|
|
24
24
|
return
|
25
25
|
end
|
26
26
|
configuration = connector_class.configurable_fields_indifferent_access
|
27
|
+
features = connector_class.kibana_features.each_with_object({}) { |feature, hsh| hsh[feature] = true }
|
27
28
|
doc = {
|
28
|
-
:configuration => configuration
|
29
|
+
:configuration => configuration,
|
30
|
+
:features => features
|
29
31
|
}
|
30
32
|
|
31
33
|
doc[:service_type] = service_type if service_type && connector_settings.needs_service_type?
|
@@ -0,0 +1,137 @@
|
|
1
|
+
#
|
2
|
+
# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
3
|
+
# or more contributor license agreements. Licensed under the Elastic License;
|
4
|
+
# you may not use this file except in compliance with the Elastic License.
|
5
|
+
#
|
6
|
+
|
7
|
+
# frozen_string_literal: true
|
8
|
+
|
9
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
10
|
+
require 'connectors/sync_status'
|
11
|
+
require 'core/elastic_connector_actions'
|
12
|
+
require 'utility'
|
13
|
+
|
14
|
+
module Core
|
15
|
+
class ConnectorJob
|
16
|
+
DEFAULT_PAGE_SIZE = 100
|
17
|
+
|
18
|
+
# Error Classes
|
19
|
+
class ConnectorJobNotFoundError < StandardError; end
|
20
|
+
|
21
|
+
def self.fetch_by_id(job_id)
|
22
|
+
es_response = ElasticConnectorActions.get_job(job_id)
|
23
|
+
|
24
|
+
raise ConnectorJobNotFoundError.new("Connector job with id=#{job_id} was not found.") unless es_response[:found]
|
25
|
+
new(es_response)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.pending_jobs(page_size = DEFAULT_PAGE_SIZE)
|
29
|
+
query = { terms: { status: Connectors::SyncStatus::PENDING_STATUES } }
|
30
|
+
fetch_jobs_by_query(query, page_size)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.orphaned_jobs(_page_size = DEFAULT_PAGE_SIZE)
|
34
|
+
[]
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.stuck_jobs(_page_size = DEFAULT_PAGE_SIZE)
|
38
|
+
[]
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.enqueue(_connector_id)
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def id
|
46
|
+
@elasticsearch_response[:_id]
|
47
|
+
end
|
48
|
+
|
49
|
+
def [](property_name)
|
50
|
+
@elasticsearch_response[:_source][property_name]
|
51
|
+
end
|
52
|
+
|
53
|
+
def status
|
54
|
+
self[:status]
|
55
|
+
end
|
56
|
+
|
57
|
+
def in_progress?
|
58
|
+
status == Connectors::SyncStatus::IN_PROGRESS
|
59
|
+
end
|
60
|
+
|
61
|
+
def canceling?
|
62
|
+
status == Connectors::SyncStatus::CANCELING
|
63
|
+
end
|
64
|
+
|
65
|
+
def connector_snapshot
|
66
|
+
self[:connector]
|
67
|
+
end
|
68
|
+
|
69
|
+
def connector_id
|
70
|
+
connector_snapshot[:id]
|
71
|
+
end
|
72
|
+
|
73
|
+
def index_name
|
74
|
+
connector_snapshot[:configuration]
|
75
|
+
end
|
76
|
+
|
77
|
+
def language
|
78
|
+
connector_snapshot[:language]
|
79
|
+
end
|
80
|
+
|
81
|
+
def service_type
|
82
|
+
connector_snapshot[:service_type]
|
83
|
+
end
|
84
|
+
|
85
|
+
def configuration
|
86
|
+
connector_snapshot[:configuration]
|
87
|
+
end
|
88
|
+
|
89
|
+
def filtering
|
90
|
+
Utility::Filtering.extract_filter(connector_snapshot[:filtering])
|
91
|
+
end
|
92
|
+
|
93
|
+
def pipeline
|
94
|
+
connector_snapshot[:pipeline]
|
95
|
+
end
|
96
|
+
|
97
|
+
def connector
|
98
|
+
@connector ||= ConnectorSettings.fetch_by_id(connector_id)
|
99
|
+
end
|
100
|
+
|
101
|
+
def reload_connector!
|
102
|
+
@connector = nil
|
103
|
+
connector
|
104
|
+
end
|
105
|
+
|
106
|
+
def reload
|
107
|
+
es_response = ElasticConnectorActions.get_job(id)
|
108
|
+
raise ConnectorJobNotFoundError.new("Connector job with id=#{id} was not found.") unless es_response[:found]
|
109
|
+
# TODO: remove the usage of with_indifferent_access. get_id method is expected to return a hash
|
110
|
+
@elasticsearch_response = es_response.with_indifferent_access
|
111
|
+
@connector = nil
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def initialize(es_response)
|
117
|
+
# TODO: remove the usage of with_indifferent_access. The initialize method should expect a hash argument
|
118
|
+
@elasticsearch_response = es_response.with_indifferent_access
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.fetch_jobs_by_query(query, page_size)
|
122
|
+
results = []
|
123
|
+
offset = 0
|
124
|
+
loop do
|
125
|
+
response = ElasticConnectorActions.search_jobs(query, page_size, offset)
|
126
|
+
|
127
|
+
hits = response.dig('hits', 'hits') || []
|
128
|
+
total = response.dig('hits', 'total', 'value') || 0
|
129
|
+
results += hits.map { |hit| new(hit) }
|
130
|
+
break if results.size >= total
|
131
|
+
offset += hits.size
|
132
|
+
end
|
133
|
+
|
134
|
+
results
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|