connectors_service 8.6.0.4.pre.20221104T200814Z → 8.6.0.4.pre.20221116T024501Z
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/app.rb +4 -0
- data/lib/app/dispatcher.rb +42 -17
- data/lib/app/preflight_check.rb +11 -0
- data/lib/connectors/base/advanced_snippet_against_schema_validator.rb +173 -0
- data/lib/connectors/base/advanced_snippet_validator.rb +34 -0
- data/lib/connectors/base/connector.rb +43 -14
- data/lib/connectors/base/simple_rules_parser.rb +42 -0
- data/lib/connectors/example/connector.rb +6 -0
- data/lib/connectors/example/example_advanced_snippet_validator.rb +35 -0
- data/lib/connectors/gitlab/connector.rb +6 -1
- data/lib/connectors/gitlab/gitlab_advanced_snippet_validator.rb +35 -0
- data/lib/connectors/mongodb/connector.rb +47 -43
- data/lib/connectors/mongodb/mongo_advanced_snippet_against_schema_validator.rb +22 -0
- data/lib/connectors/mongodb/mongo_advanced_snippet_schema.rb +292 -0
- data/lib/connectors/mongodb/mongo_rules_parser.rb +81 -0
- data/lib/connectors/sync_status.rb +6 -1
- data/lib/connectors/tolerable_error_helper.rb +43 -0
- data/lib/core/configuration.rb +3 -1
- data/lib/core/connector_job.rb +210 -0
- data/lib/core/connector_settings.rb +52 -16
- data/lib/core/elastic_connector_actions.rb +320 -59
- 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 +118 -0
- data/lib/core/{output_sink.rb → ingestion.rb} +1 -5
- data/lib/core/jobs/consumer.rb +114 -0
- data/lib/core/jobs/producer.rb +26 -0
- data/lib/core/scheduler.rb +40 -10
- data/lib/core/single_scheduler.rb +1 -1
- data/lib/core/sync_job_runner.rb +72 -16
- data/lib/core.rb +4 -0
- data/lib/utility/bulk_queue.rb +85 -0
- data/lib/utility/constants.rb +2 -0
- data/lib/utility/error_monitor.rb +108 -0
- data/lib/utility/errors.rb +0 -12
- data/lib/utility/filtering.rb +22 -0
- data/lib/utility/logger.rb +1 -1
- data/lib/utility.rb +11 -4
- metadata +25 -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: 9a610999d9c34543392e1dd9b7b9ab136557992cc1503fa5aa5973186d17af9e
|
4
|
+
data.tar.gz: '086282a4a15ce1b168e6a0add5f78646e1fe89c336b8a257677826cce3677635'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf0a0a22ea96074bcbb904000ec489a591fc8491bc2d91759bc1a097c60e31c2abad7b118bb97d16bbe80c5b00dbe08f7c65653710c2d6a77d3356607d87e045
|
7
|
+
data.tar.gz: 67ff773ed2ae3f869897b5fb3a2c0dc3fc55c5922c602e032ed5bafa800043bd4b7f4af90fdd055e575ea877f3cf3b9e308d91c58bad59cd6c63dd5eb8ab98f4
|
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-20221116T024501Z
|
3
3
|
repository: git@github.com:elastic/ent-search-connectors.git
|
4
|
-
revision:
|
4
|
+
revision: b3cc1332879a38930a272a63f8c6be1847578204
|
5
5
|
elasticsearch:
|
6
6
|
hosts: http://localhost:9200
|
7
|
-
api_key:
|
7
|
+
api_key: MDJyaGZZUUJ1ZXdOLTB1ZnQ5aXQ6a2IwQURWSjVUc1NPcjVsOFBuVDBzZw==
|
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: 0mrhfYQBuewN-0ufptgN
|
24
|
+
service_type: mongodb
|
data/lib/app/app.rb
CHANGED
@@ -17,6 +17,10 @@ require 'utility/logger'
|
|
17
17
|
module App
|
18
18
|
Utility::Environment.set_execution_environment(App::Config) do
|
19
19
|
App::PreflightCheck.run!
|
20
|
+
|
21
|
+
# set exit hook
|
22
|
+
Kernel.at_exit { App::Dispatcher.shutdown! }
|
23
|
+
|
20
24
|
App::Dispatcher.start!
|
21
25
|
rescue App::PreflightCheck::CheckFailure => e
|
22
26
|
Utility::Logger.error("Preflight check failed: #{e.message}")
|
data/lib/app/dispatcher.rb
CHANGED
@@ -28,6 +28,10 @@ module App
|
|
28
28
|
def start!
|
29
29
|
running!
|
30
30
|
Utility::Logger.info("Starting connector service in #{App::Config.native_mode ? 'native' : 'non-native'} mode...")
|
31
|
+
|
32
|
+
# start sync jobs consumer
|
33
|
+
start_consumer!
|
34
|
+
|
31
35
|
start_polling_jobs!
|
32
36
|
end
|
33
37
|
|
@@ -37,6 +41,8 @@ module App
|
|
37
41
|
scheduler.shutdown
|
38
42
|
pool.shutdown
|
39
43
|
pool.wait_for_termination(TERMINATION_TIMEOUT)
|
44
|
+
|
45
|
+
stop_consumer!
|
40
46
|
end
|
41
47
|
|
42
48
|
private
|
@@ -68,11 +74,16 @@ module App
|
|
68
74
|
scheduler.when_triggered do |connector_settings, task|
|
69
75
|
case task
|
70
76
|
when :sync
|
71
|
-
|
77
|
+
# update connector sync_now flag
|
78
|
+
Core::ElasticConnectorActions.update_connector_sync_now(connector_settings.id, false)
|
79
|
+
|
80
|
+
Core::Jobs::Producer.enqueue_job(job_type: :sync, connector_settings: connector_settings)
|
72
81
|
when :heartbeat
|
73
82
|
start_heartbeat_task(connector_settings)
|
74
83
|
when :configuration
|
75
84
|
start_configuration_task(connector_settings)
|
85
|
+
when :filter_validation
|
86
|
+
start_filter_validation_task(connector_settings)
|
76
87
|
else
|
77
88
|
Utility::Logger.error("Unknown task type: #{task}. Skipping...")
|
78
89
|
end
|
@@ -81,22 +92,6 @@ module App
|
|
81
92
|
Utility::ExceptionTracking.log_exception(e, 'The connector service failed due to unexpected error.')
|
82
93
|
end
|
83
94
|
|
84
|
-
def start_sync_task(connector_settings)
|
85
|
-
start_heartbeat_task(connector_settings)
|
86
|
-
pool.post do
|
87
|
-
Utility::Logger.info("Initiating a sync job for #{connector_settings.formatted}...")
|
88
|
-
Core::ElasticConnectorActions.ensure_content_index_exists(connector_settings.index_name)
|
89
|
-
job_runner = Core::SyncJobRunner.new(connector_settings)
|
90
|
-
job_runner.execute
|
91
|
-
rescue Core::JobAlreadyRunningError
|
92
|
-
Utility::Logger.info("Sync job for #{connector_settings.formatted} is already running, skipping.")
|
93
|
-
rescue Core::ConnectorVersionChangedError => e
|
94
|
-
Utility::Logger.info("Could not start the job because #{connector_settings.formatted} has been updated externally. Message: #{e.message}")
|
95
|
-
rescue StandardError => e
|
96
|
-
Utility::ExceptionTracking.log_exception(e, "Sync job for #{connector_settings.formatted} failed due to unexpected error.")
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
95
|
def start_heartbeat_task(connector_settings)
|
101
96
|
pool.post do
|
102
97
|
Utility::Logger.info("Sending heartbeat for #{connector_settings.formatted}...")
|
@@ -120,6 +115,36 @@ module App
|
|
120
115
|
Utility::ExceptionTracking.log_exception(e, "Configuration task for #{connector_settings.formatted} failed due to unexpected error.")
|
121
116
|
end
|
122
117
|
end
|
118
|
+
|
119
|
+
def start_filter_validation_task(connector_settings)
|
120
|
+
pool.post do
|
121
|
+
Utility::Logger.info("Validating filters for #{connector_settings.formatted}...")
|
122
|
+
validation_job_runner = Core::Filtering::ValidationJobRunner.new(connector_settings)
|
123
|
+
validation_job_runner.execute
|
124
|
+
rescue StandardError => e
|
125
|
+
Utility::ExceptionTracking.log_exception(e, "Filter validation task for #{connector_settings.formatted} failed due to unexpected error.")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def start_consumer!
|
130
|
+
@consumer = Core::Jobs::Consumer.new(
|
131
|
+
poll_interval: POLL_INTERVAL,
|
132
|
+
termination_timeout: TERMINATION_TIMEOUT,
|
133
|
+
min_threads: MIN_THREADS,
|
134
|
+
max_threads: MAX_THREADS,
|
135
|
+
max_queue: MAX_QUEUE,
|
136
|
+
scheduler: scheduler
|
137
|
+
)
|
138
|
+
|
139
|
+
@consumer.subscribe!(index_name: Utility::Constants::JOB_INDEX)
|
140
|
+
end
|
141
|
+
|
142
|
+
def stop_consumer!
|
143
|
+
return if @consumer.nil?
|
144
|
+
return unless @consumer.running?
|
145
|
+
|
146
|
+
@consumer.shutdown!
|
147
|
+
end
|
123
148
|
end
|
124
149
|
end
|
125
150
|
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
|
|
@@ -0,0 +1,173 @@
|
|
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'
|
9
|
+
require 'utility/logger'
|
10
|
+
require 'connectors/base/advanced_snippet_validator'
|
11
|
+
require 'core/filtering/validation_status'
|
12
|
+
|
13
|
+
module Connectors
|
14
|
+
module Base
|
15
|
+
class AdvancedSnippetAgainstSchemaValidator < Connectors::Base::AdvancedSnippetValidator
|
16
|
+
|
17
|
+
MAX_RECURSION_DEPTH = 50
|
18
|
+
ADVANCED_SNIPPET_ID = 'advanced_snippet'
|
19
|
+
|
20
|
+
def initialize(advanced_snippet, schema)
|
21
|
+
super(advanced_snippet)
|
22
|
+
@schema = schema
|
23
|
+
end
|
24
|
+
|
25
|
+
def is_snippet_valid?
|
26
|
+
validation_result = validate_against_schema(@schema, @advanced_snippet)
|
27
|
+
log_validation_result(validation_result)
|
28
|
+
validation_result
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def validate_against_schema(config_schema, advanced_snippet, recursion_depth = 0)
|
34
|
+
# Prevent unintentional/intentional SystemStackErrors/crashes
|
35
|
+
return unexpected_error if exceeded_recursion_depth?(recursion_depth)
|
36
|
+
|
37
|
+
return valid_snippet if config_schema.nil? || config_schema.empty?
|
38
|
+
|
39
|
+
schema_fields = config_schema[:fields].is_a?(Hash) ? config_schema.dig(:fields, :values) : config_schema[:fields]
|
40
|
+
snippet_field_names = advanced_snippet&.keys&.map(&:to_s)
|
41
|
+
schema_field_names = schema_fields.map { |field| field[:name] }
|
42
|
+
|
43
|
+
return unexpected_field(schema_field_names, snippet_field_names) if unexpected_field_present?(snippet_field_names, schema_field_names)
|
44
|
+
|
45
|
+
return fields_constraint_violation(config_schema[:fields]) if fields_constraints_violated?(config_schema, advanced_snippet)
|
46
|
+
|
47
|
+
schema_fields.each do |field|
|
48
|
+
name = field[:name]
|
49
|
+
type = field[:type]
|
50
|
+
optional = field[:optional] || false
|
51
|
+
|
52
|
+
snippet_field_value = advanced_snippet.with_indifferent_access[name]
|
53
|
+
|
54
|
+
next if optional && (snippet_field_value.nil? || !snippet_field_value.present?)
|
55
|
+
|
56
|
+
return wrong_names(snippet_field_names, name) unless snippet_field_names.include?(name)
|
57
|
+
|
58
|
+
return wrong_type(name, type, snippet_field_value) if type_error_present?(type, snippet_field_value)
|
59
|
+
|
60
|
+
if field[:fields].present?
|
61
|
+
validation_result = validate_against_schema(field, snippet_field_value, recursion_depth + 1)
|
62
|
+
|
63
|
+
return validation_result unless validation_result[:is_valid]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
valid_snippet
|
68
|
+
end
|
69
|
+
|
70
|
+
def fields_constraints_violated?(config_schema, advanced_snippet)
|
71
|
+
return false unless config_schema[:fields].is_a?(Hash)
|
72
|
+
|
73
|
+
constraints = config_schema.dig(:fields, :constraints)
|
74
|
+
constraints = constraints.is_a?(Array) ? constraints : [constraints]
|
75
|
+
|
76
|
+
constraints.each do |constraint|
|
77
|
+
return true unless constraint.call(advanced_snippet)
|
78
|
+
end
|
79
|
+
|
80
|
+
false
|
81
|
+
end
|
82
|
+
|
83
|
+
def type_error_present?(schema_type, snippet_value)
|
84
|
+
return !schema_type.call(snippet_value) if schema_type.is_a?(Proc)
|
85
|
+
|
86
|
+
!snippet_value.is_a?(schema_type)
|
87
|
+
end
|
88
|
+
|
89
|
+
def exceeded_recursion_depth?(recursion_depth)
|
90
|
+
if recursion_depth >= MAX_RECURSION_DEPTH
|
91
|
+
Utility::Logger.warn("Recursion depth for filtering validation exceeded. (Max recursion depth: #{MAX_RECURSION_DEPTH})")
|
92
|
+
return true
|
93
|
+
end
|
94
|
+
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
98
|
+
def unexpected_field_present?(actual_field_names, expected_field_names)
|
99
|
+
difference = actual_field_names - expected_field_names
|
100
|
+
|
101
|
+
# we have field names, which we didn't expect
|
102
|
+
!difference.empty?
|
103
|
+
end
|
104
|
+
|
105
|
+
def valid_snippet
|
106
|
+
{
|
107
|
+
:state => Core::Filtering::ValidationStatus::VALID,
|
108
|
+
:errors => []
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
def unexpected_field(expected_fields, actual_fields)
|
113
|
+
{
|
114
|
+
:state => Core::Filtering::ValidationStatus::INVALID,
|
115
|
+
:errors => [
|
116
|
+
{
|
117
|
+
:ids => [ADVANCED_SNIPPET_ID],
|
118
|
+
:messages => ["Encountered unexpected fields '#{actual_fields}'. Expected: '#{expected_fields}'."]
|
119
|
+
}
|
120
|
+
]
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
def wrong_type(field_name, expected_type, actual_value)
|
125
|
+
{
|
126
|
+
:state => Core::Filtering::ValidationStatus::INVALID,
|
127
|
+
:errors => [
|
128
|
+
{
|
129
|
+
:ids => [ADVANCED_SNIPPET_ID],
|
130
|
+
:messages => ["Expected field type '#{expected_type.is_a?(Proc) ? 'custom matcher' : expected_type}' for field '#{field_name}', but got value '#{actual_value.inspect}' of type '#{actual_value.class}'."]
|
131
|
+
}
|
132
|
+
]
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
def wrong_names(actual_field_names, expected_field_name)
|
137
|
+
{
|
138
|
+
:state => Core::Filtering::ValidationStatus::INVALID,
|
139
|
+
:errors => [
|
140
|
+
{
|
141
|
+
:ids => [ADVANCED_SNIPPET_ID],
|
142
|
+
:messages => ["Expected field name '#{expected_field_name}', but got #{actual_field_names}."]
|
143
|
+
}
|
144
|
+
]
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
def fields_constraint_violation(fields)
|
149
|
+
{
|
150
|
+
:state => Core::Filtering::ValidationStatus::INVALID,
|
151
|
+
:errors => [
|
152
|
+
{
|
153
|
+
:ids => [ADVANCED_SNIPPET_ID],
|
154
|
+
:messages => ["A fields constraint was violated for fields: '#{fields[:values].map { |v| v[:name] }}'. Check advanced snippet field constraints."]
|
155
|
+
}
|
156
|
+
]
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
def unexpected_error
|
161
|
+
{
|
162
|
+
:state => Core::Filtering::ValidationStatus::INVALID,
|
163
|
+
:errors => [
|
164
|
+
{
|
165
|
+
:ids => [ADVANCED_SNIPPET_ID],
|
166
|
+
:messages => ['Unexpected error. Check logs for details.']
|
167
|
+
}
|
168
|
+
]
|
169
|
+
}
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,34 @@
|
|
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 'utility/logger'
|
9
|
+
|
10
|
+
module Connectors
|
11
|
+
module Base
|
12
|
+
class AdvancedSnippetValidator
|
13
|
+
|
14
|
+
def initialize(advanced_snippet)
|
15
|
+
@advanced_snippet = advanced_snippet || {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def is_snippet_valid?
|
19
|
+
raise 'Advanced Snippet validation not implemented'
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def log_validation_result(validation_result)
|
25
|
+
Utility::Logger.info("Filtering Advanced Configuration validation result: #{validation_result[:state]}")
|
26
|
+
if validation_result[:errors].present?
|
27
|
+
validation_result[:errors].each do |error|
|
28
|
+
Utility::Logger.warn("Validation error for: '#{error[:ids]}': '#{error[:messages]}'")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -6,11 +6,15 @@
|
|
6
6
|
|
7
7
|
# frozen_string_literal: true
|
8
8
|
|
9
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
10
|
+
require 'app/config'
|
9
11
|
require 'bson'
|
10
|
-
require '
|
12
|
+
require 'connectors/base/advanced_snippet_validator'
|
13
|
+
require 'core/ingestion'
|
14
|
+
require 'connectors/tolerable_error_helper'
|
15
|
+
require 'core/filtering/validation_status'
|
11
16
|
require 'utility'
|
12
|
-
require '
|
13
|
-
require 'active_support/core_ext/hash/indifferent_access'
|
17
|
+
require 'utility/filtering'
|
14
18
|
|
15
19
|
module Connectors
|
16
20
|
module Base
|
@@ -32,20 +36,50 @@ module Connectors
|
|
32
36
|
raise 'Not implemented for this connector'
|
33
37
|
end
|
34
38
|
|
39
|
+
def self.kibana_features
|
40
|
+
[
|
41
|
+
Utility::Constants::FILTERING_RULES_FEATURE,
|
42
|
+
Utility::Constants::FILTERING_ADVANCED_FEATURE
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.advanced_snippet_validator
|
47
|
+
AdvancedSnippetValidator
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.validate_filtering(filtering = {})
|
51
|
+
# nothing to validate
|
52
|
+
return { :state => Core::Filtering::ValidationStatus::VALID, :errors => [] } unless filtering.present?
|
53
|
+
|
54
|
+
filter = Utility::Filtering.extract_filter(filtering)
|
55
|
+
advanced_snippet = filter.dig(:advanced_snippet, :value)
|
56
|
+
|
57
|
+
snippet_validator_instance = advanced_snippet_validator.new(advanced_snippet)
|
58
|
+
|
59
|
+
snippet_validator_instance.is_snippet_valid?
|
60
|
+
end
|
61
|
+
|
35
62
|
attr_reader :rules, :advanced_filter_config
|
36
63
|
|
37
64
|
def initialize(configuration: {}, job_description: {})
|
65
|
+
error_monitor = Utility::ErrorMonitor.new
|
66
|
+
@tolerable_error_helper = Connectors::TolerableErrorHelper.new(error_monitor)
|
67
|
+
|
38
68
|
@configuration = configuration.dup || {}
|
39
69
|
@job_description = job_description&.dup || {}
|
40
70
|
|
41
|
-
|
71
|
+
filtering = Utility::Filtering.extract_filter(@job_description.dig(:connector, :filtering))
|
42
72
|
|
43
|
-
@rules =
|
44
|
-
@advanced_filter_config =
|
73
|
+
@rules = filtering[:rules] || []
|
74
|
+
@advanced_filter_config = filtering.dig(:advanced_snippet, :value) || {}
|
45
75
|
end
|
46
76
|
|
47
77
|
def yield_documents; end
|
48
78
|
|
79
|
+
def yield_with_handling_tolerable_errors(identifier: nil, &block)
|
80
|
+
@tolerable_error_helper.yield_single_document(identifier: identifier, &block)
|
81
|
+
end
|
82
|
+
|
49
83
|
def do_health_check
|
50
84
|
raise 'Not implemented for this connector'
|
51
85
|
end
|
@@ -67,16 +101,11 @@ module Connectors
|
|
67
101
|
end
|
68
102
|
|
69
103
|
def filtering_present?
|
70
|
-
@advanced_filter_config.present? || @rules.present?
|
104
|
+
@advanced_filter_config.present? && !@advanced_filter_config.empty? || @rules.present?
|
71
105
|
end
|
72
106
|
|
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 : {}
|
107
|
+
def metadata
|
108
|
+
{}
|
80
109
|
end
|
81
110
|
end
|
82
111
|
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,8 @@
|
|
7
7
|
# frozen_string_literal: true
|
8
8
|
|
9
9
|
require 'connectors/base/connector'
|
10
|
+
require 'connectors/example/example_advanced_snippet_validator'
|
11
|
+
require 'core/filtering/validation_status'
|
10
12
|
require 'utility'
|
11
13
|
|
12
14
|
module Connectors
|
@@ -45,6 +47,10 @@ module Connectors
|
|
45
47
|
# raise 'something went wrong'
|
46
48
|
end
|
47
49
|
|
50
|
+
def self.advanced_snippet_validator
|
51
|
+
ExampleAdvancedSnippetValidator
|
52
|
+
end
|
53
|
+
|
48
54
|
def yield_documents
|
49
55
|
attachments = [
|
50
56
|
load_attachment('first_attachment.txt'),
|
@@ -0,0 +1,35 @@
|
|
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 'connectors/base/advanced_snippet_validator'
|
10
|
+
|
11
|
+
module Connectors
|
12
|
+
module Example
|
13
|
+
class ExampleAdvancedSnippetValidator < Connectors::Base::AdvancedSnippetValidator
|
14
|
+
|
15
|
+
def is_snippet_valid?
|
16
|
+
# TODO: real filtering validation will follow later
|
17
|
+
errors = [
|
18
|
+
{
|
19
|
+
:ids => ['missing-implementation'],
|
20
|
+
:messages => ['Filtering is not implemented yet for the example connector']
|
21
|
+
}
|
22
|
+
]
|
23
|
+
|
24
|
+
validation_result = if @advanced_snippet.present? && !@advanced_snippet.empty?
|
25
|
+
{ :state => Core::Filtering::ValidationStatus::INVALID, :errors => errors }
|
26
|
+
else
|
27
|
+
{ :state => Core::Filtering::ValidationStatus::VALID, :errors => [] }
|
28
|
+
end
|
29
|
+
log_validation_result(validation_result)
|
30
|
+
validation_result
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -11,7 +11,8 @@ 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 '
|
14
|
+
require 'connectors/gitlab/gitlab_advanced_snippet_validator'
|
15
|
+
require 'core/ingestion'
|
15
16
|
|
16
17
|
module Connectors
|
17
18
|
module GitLab
|
@@ -36,6 +37,10 @@ module Connectors
|
|
36
37
|
}
|
37
38
|
end
|
38
39
|
|
40
|
+
def self.advanced_snippet_validator
|
41
|
+
GitLabAdvancedSnippetValidator
|
42
|
+
end
|
43
|
+
|
39
44
|
def initialize(configuration: {}, job_description: {})
|
40
45
|
super
|
41
46
|
|
@@ -0,0 +1,35 @@
|
|
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 'connectors/base/advanced_snippet_validator'
|
10
|
+
|
11
|
+
module Connectors
|
12
|
+
module GitLab
|
13
|
+
class GitLabAdvancedSnippetValidator < Connectors::Base::AdvancedSnippetValidator
|
14
|
+
|
15
|
+
def is_snippet_valid?
|
16
|
+
# TODO: real filtering validation will follow later
|
17
|
+
errors = [
|
18
|
+
{
|
19
|
+
:ids => ['missing-implementation'],
|
20
|
+
:messages => ['Filtering is not implemented yet for the GitLab connector']
|
21
|
+
}
|
22
|
+
]
|
23
|
+
|
24
|
+
validation_result = if @advanced_snippet.present? && !@advanced_snippet.empty?
|
25
|
+
{ :state => Core::Filtering::ValidationStatus::INVALID, :errors => errors }
|
26
|
+
else
|
27
|
+
{ :state => Core::Filtering::ValidationStatus::VALID, :errors => [] }
|
28
|
+
end
|
29
|
+
log_validation_result(validation_result)
|
30
|
+
validation_result
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|