connectors_service 8.7.0.0.pre.20221117T010623Z → 8.11.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/connectors.yml +10 -8
- data/lib/app/config.rb +6 -1
- data/lib/app/console_app.rb +1 -1
- data/lib/app/dispatcher.rb +18 -3
- data/lib/connectors/base/connector.rb +39 -22
- data/lib/connectors/crawler/scheduler.rb +36 -0
- data/lib/connectors/example/connector.rb +2 -2
- data/lib/connectors/example/example_advanced_snippet_validator.rb +4 -3
- data/lib/connectors/gitlab/connector.rb +4 -4
- data/lib/connectors/gitlab/gitlab_advanced_snippet_validator.rb +8 -10
- data/lib/{connectors_app/// → connectors/job_trigger_method.rb} +6 -5
- data/lib/connectors/mongodb/connector.rb +66 -56
- data/lib/connectors/mongodb/mongo_advanced_snippet_against_schema_validator.rb +2 -2
- data/lib/connectors/mongodb/mongo_advanced_snippet_schema.rb +3 -2
- data/lib/connectors/mongodb/mongo_advanced_snippet_snake_case_transformer.rb +49 -0
- data/lib/connectors/registry.rb +1 -1
- data/lib/connectors/tolerable_error_helper.rb +5 -1
- data/lib/connectors_utility.rb +6 -3
- data/lib/core/configuration.rb +13 -1
- data/lib/core/connector_job.rb +48 -7
- data/lib/core/connector_settings.rb +52 -20
- data/lib/core/elastic_connector_actions.rb +54 -38
- data/lib/core/filtering/advanced_snippet/advanced_snippet_against_schema_validator.rb +32 -0
- data/lib/core/filtering/advanced_snippet/advanced_snippet_validator.rb +27 -0
- data/lib/core/filtering/filter_validator.rb +103 -0
- data/lib/{connectors/base/advanced_snippet_against_schema_validator.rb → core/filtering/hash_against_schema_validator.rb} +58 -44
- data/lib/core/filtering/post_process_engine.rb +2 -2
- data/lib/core/filtering/processing_stage.rb +20 -0
- data/lib/core/filtering/{simple_rule.rb → simple_rules/simple_rule.rb} +34 -1
- data/lib/core/filtering/simple_rules/simple_rules_parser.rb +44 -0
- data/lib/core/filtering/simple_rules/validation/no_conflicting_policies_rules_validator.rb +47 -0
- data/lib/core/filtering/simple_rules/validation/simple_rules_schema.rb +68 -0
- data/lib/core/filtering/simple_rules/validation/simple_rules_validator.rb +25 -0
- data/lib/core/filtering/simple_rules/validation/single_rule_against_schema_validator.rb +37 -0
- data/lib/core/filtering/transform/filter_transformer.rb +26 -0
- data/lib/core/filtering/transform/filter_transformer_facade.rb +61 -0
- data/lib/core/filtering/transform/transformation_target.rb +10 -0
- data/lib/core/filtering/validation_job_runner.rb +1 -3
- data/lib/core/filtering.rb +5 -3
- data/lib/core/job_cleanup.rb +66 -0
- data/lib/core/jobs/consumer.rb +62 -64
- data/lib/core/jobs/producer.rb +3 -0
- data/lib/core/scheduler.rb +67 -52
- data/lib/core/sync_job_runner.rb +170 -83
- data/lib/core.rb +1 -0
- data/lib/utility/bulk_queue.rb +1 -1
- data/lib/utility/constants.rb +0 -2
- data/lib/utility/error_monitor.rb +26 -5
- data/lib/utility/es_client.rb +4 -0
- data/lib/utility/filtering.rb +4 -0
- metadata +32 -21
- data/lib/connectors/base/advanced_snippet_validator.rb +0 -34
- data/lib/connectors/base/simple_rules_parser.rb +0 -42
- data/lib/connectors/mongodb/mongo_rules_parser.rb +0 -81
@@ -0,0 +1,49 @@
|
|
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 'core/filtering/transform/filter_transformer'
|
10
|
+
require 'active_support'
|
11
|
+
|
12
|
+
module Connectors
|
13
|
+
module MongoDB
|
14
|
+
class MongoAdvancedSnippetSnakeCaseTransformer < Core::Filtering::Transform::FilterTransformer
|
15
|
+
|
16
|
+
def initialize(advanced_snippet = {})
|
17
|
+
super
|
18
|
+
|
19
|
+
@advanced_snippet = advanced_snippet
|
20
|
+
@transformation = ->(snippet) { snake_case_filter(snippet) }
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def snake_case_filter(advanced_snippet, transformed_filter = {})
|
26
|
+
advanced_snippet&.each do |key, value|
|
27
|
+
snake_case_key = key.to_s.underscore
|
28
|
+
|
29
|
+
value = value.is_a?(Hash) ? snake_case_filter(value, {}) : value
|
30
|
+
|
31
|
+
if value.is_a?(Array)
|
32
|
+
new_entries = []
|
33
|
+
|
34
|
+
value.each do |entry|
|
35
|
+
new_entry = entry.is_a?(Hash) ? snake_case_filter(entry, {}) : entry
|
36
|
+
new_entries.push(new_entry)
|
37
|
+
end
|
38
|
+
|
39
|
+
value = new_entries
|
40
|
+
end
|
41
|
+
|
42
|
+
transformed_filter[snake_case_key] = value
|
43
|
+
end
|
44
|
+
|
45
|
+
transformed_filter
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/connectors/registry.rb
CHANGED
@@ -24,7 +24,7 @@ module Connectors
|
|
24
24
|
@connectors[name]
|
25
25
|
end
|
26
26
|
|
27
|
-
def connector(name, configuration, job_description:
|
27
|
+
def connector(name, configuration, job_description: nil)
|
28
28
|
klass = connector_class(name)
|
29
29
|
if klass.present?
|
30
30
|
return klass.new(configuration: configuration, job_description: job_description)
|
@@ -36,7 +36,11 @@ module Connectors
|
|
36
36
|
|
37
37
|
def fatal_exception_classes
|
38
38
|
[
|
39
|
-
Utility::ErrorMonitor::MonitoringError
|
39
|
+
Utility::ErrorMonitor::MonitoringError,
|
40
|
+
Core::ConnectorNotFoundError,
|
41
|
+
Core::ConnectorJobNotFoundError,
|
42
|
+
Core::ConnectorJobCanceledError,
|
43
|
+
Core::ConnectorJobNotRunningError
|
40
44
|
]
|
41
45
|
end
|
42
46
|
end
|
data/lib/connectors_utility.rb
CHANGED
@@ -9,8 +9,11 @@
|
|
9
9
|
require_relative 'utility'
|
10
10
|
|
11
11
|
require_relative 'connectors/connector_status'
|
12
|
+
require_relative 'connectors/crawler/scheduler'
|
13
|
+
require_relative 'connectors/job_trigger_method'
|
12
14
|
require_relative 'connectors/sync_status'
|
13
|
-
require_relative 'core/
|
15
|
+
require_relative 'core/connector_job'
|
16
|
+
require_relative 'core/connector_settings'
|
14
17
|
require_relative 'core/elastic_connector_actions'
|
15
|
-
|
16
|
-
require_relative '
|
18
|
+
require_relative 'core/filtering/validation_status'
|
19
|
+
require_relative 'core/scheduler'
|
data/lib/core/configuration.rb
CHANGED
@@ -24,7 +24,19 @@ module Core
|
|
24
24
|
return
|
25
25
|
end
|
26
26
|
configuration = connector_class.configurable_fields_indifferent_access
|
27
|
-
|
27
|
+
|
28
|
+
features = {}
|
29
|
+
|
30
|
+
connector_class.kibana_features.each do |feature_definition, _hsh|
|
31
|
+
feature = feature_definition[:feature]
|
32
|
+
subfeature = feature_definition[:subfeature]
|
33
|
+
enabled = feature_definition[:enabled]
|
34
|
+
|
35
|
+
features[feature] = {} unless features.key?(feature)
|
36
|
+
|
37
|
+
features[feature][subfeature] = { :enabled => enabled }
|
38
|
+
end
|
39
|
+
|
28
40
|
doc = {
|
29
41
|
:configuration => configuration,
|
30
42
|
:features => features
|
data/lib/core/connector_job.rb
CHANGED
@@ -8,12 +8,14 @@
|
|
8
8
|
|
9
9
|
require 'active_support/core_ext/hash/indifferent_access'
|
10
10
|
require 'connectors/sync_status'
|
11
|
+
require 'core/connector_settings'
|
11
12
|
require 'core/elastic_connector_actions'
|
12
13
|
require 'utility'
|
13
14
|
|
14
15
|
module Core
|
15
16
|
class ConnectorJob
|
16
17
|
DEFAULT_PAGE_SIZE = 100
|
18
|
+
IDLE_THRESHOLD = 60
|
17
19
|
|
18
20
|
def self.fetch_by_id(job_id)
|
19
21
|
es_response = ElasticConnectorActions.get_job(job_id)
|
@@ -34,12 +36,32 @@ module Core
|
|
34
36
|
fetch_jobs_by_query(query, page_size)
|
35
37
|
end
|
36
38
|
|
37
|
-
def self.orphaned_jobs(
|
38
|
-
|
39
|
+
def self.orphaned_jobs(connector_ids = [], page_size = DEFAULT_PAGE_SIZE)
|
40
|
+
query = { bool: { must_not: { terms: { 'connector.id': connector_ids } } } }
|
41
|
+
fetch_jobs_by_query(query, page_size)
|
39
42
|
end
|
40
43
|
|
41
|
-
def self.
|
42
|
-
|
44
|
+
def self.delete_jobs(jobs)
|
45
|
+
query = { terms: { '_id': jobs.map(&:id) } }
|
46
|
+
ElasticConnectorActions.delete_jobs_by_query(query)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.idle_jobs(connector_id = nil, page_size = DEFAULT_PAGE_SIZE)
|
50
|
+
connector_ids = if connector_id
|
51
|
+
[connector_id]
|
52
|
+
else
|
53
|
+
ConnectorSettings.fetch_native_connectors.map(&:id)
|
54
|
+
end
|
55
|
+
query = {
|
56
|
+
bool: {
|
57
|
+
filter: [
|
58
|
+
{ terms: { 'connector.id': connector_ids } },
|
59
|
+
{ terms: { status: Connectors::SyncStatus::ACTIVE_STATUSES } },
|
60
|
+
{ range: { last_seen: { lte: "now-#{IDLE_THRESHOLD}s" } } }
|
61
|
+
]
|
62
|
+
}
|
63
|
+
}
|
64
|
+
fetch_jobs_by_query(query, page_size)
|
43
65
|
end
|
44
66
|
|
45
67
|
def self.enqueue(_connector_id)
|
@@ -95,7 +117,7 @@ module Core
|
|
95
117
|
end
|
96
118
|
|
97
119
|
def connector_id
|
98
|
-
|
120
|
+
connector_snapshot[:id]
|
99
121
|
end
|
100
122
|
|
101
123
|
def index_name
|
@@ -115,17 +137,36 @@ module Core
|
|
115
137
|
end
|
116
138
|
|
117
139
|
def filtering
|
118
|
-
|
140
|
+
connector_snapshot[:filtering]
|
119
141
|
end
|
120
142
|
|
121
143
|
def pipeline
|
122
|
-
|
144
|
+
connector_snapshot[:pipeline] || {}
|
145
|
+
end
|
146
|
+
|
147
|
+
def extract_binary_content?
|
148
|
+
pipeline[:extract_binary_content]
|
149
|
+
end
|
150
|
+
|
151
|
+
def reduce_whitespace?
|
152
|
+
pipeline[:reduce_whitespace]
|
153
|
+
end
|
154
|
+
|
155
|
+
def run_ml_inference?
|
156
|
+
pipeline[:run_ml_inference]
|
123
157
|
end
|
124
158
|
|
125
159
|
def connector
|
126
160
|
@connector ||= ConnectorSettings.fetch_by_id(connector_id)
|
127
161
|
end
|
128
162
|
|
163
|
+
def update_metadata(ingestion_stats = {}, connector_metadata = {})
|
164
|
+
ingestion_stats ||= {}
|
165
|
+
doc = { :last_seen => Time.now }.merge(ingestion_stats)
|
166
|
+
doc[:metadata] = connector_metadata if connector_metadata&.any?
|
167
|
+
ElasticConnectorActions.update_job_fields(id, doc)
|
168
|
+
end
|
169
|
+
|
129
170
|
def done!(ingestion_stats = {}, connector_metadata = {})
|
130
171
|
terminate!(Connectors::SyncStatus::COMPLETED, nil, ingestion_stats, connector_metadata)
|
131
172
|
end
|
@@ -8,6 +8,7 @@
|
|
8
8
|
|
9
9
|
require 'active_support/core_ext/hash/indifferent_access'
|
10
10
|
require 'connectors/connector_status'
|
11
|
+
require 'connectors/sync_status'
|
11
12
|
require 'core/elastic_connector_actions'
|
12
13
|
require 'utility'
|
13
14
|
|
@@ -49,6 +50,11 @@ module Core
|
|
49
50
|
fetch_connectors_by_query(query, page_size)
|
50
51
|
end
|
51
52
|
|
53
|
+
def self.fetch_all_connectors(page_size = DEFAULT_PAGE_SIZE)
|
54
|
+
query = { match_all: {} }
|
55
|
+
fetch_connectors_by_query(query, page_size)
|
56
|
+
end
|
57
|
+
|
52
58
|
def id
|
53
59
|
@elasticsearch_response[:_id]
|
54
60
|
end
|
@@ -58,6 +64,24 @@ module Core
|
|
58
64
|
@elasticsearch_response[:_source][property_name]
|
59
65
|
end
|
60
66
|
|
67
|
+
def features
|
68
|
+
self[:features] || {}
|
69
|
+
end
|
70
|
+
|
71
|
+
# .dig version is the modern features way of doing things,
|
72
|
+
# Right-hand of OR operator is legacy features support
|
73
|
+
# When this is fixed with a migration, we can go ahead
|
74
|
+
def filtering_rule_feature_enabled?
|
75
|
+
!!features.dig(:sync_rules, :basic, :enabled) || !!features[:filtering_rules]
|
76
|
+
end
|
77
|
+
def filtering_advanced_config_feature_enabled?
|
78
|
+
!!features.dig(:sync_rules, :advanced, :enabled) || !!features[:filtering_advanced_config]
|
79
|
+
end
|
80
|
+
|
81
|
+
def any_filtering_feature_enabled?
|
82
|
+
filtering_rule_feature_enabled? || filtering_advanced_config_feature_enabled?
|
83
|
+
end
|
84
|
+
|
61
85
|
def index_name
|
62
86
|
self[:index_name]
|
63
87
|
end
|
@@ -79,30 +103,34 @@ module Core
|
|
79
103
|
end
|
80
104
|
|
81
105
|
def scheduling_settings
|
82
|
-
self[:scheduling]
|
106
|
+
self[:scheduling] || {}
|
83
107
|
end
|
84
108
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
109
|
+
def full_sync_scheduling
|
110
|
+
scheduling_settings[:full]
|
111
|
+
end
|
88
112
|
|
89
|
-
|
113
|
+
def custom_scheduling_settings
|
114
|
+
self[:custom_scheduling]
|
90
115
|
end
|
91
116
|
|
92
|
-
def
|
93
|
-
|
117
|
+
def sync_now?
|
118
|
+
self[:sync_now] == true
|
94
119
|
end
|
95
120
|
|
96
|
-
def
|
97
|
-
|
121
|
+
def last_synced
|
122
|
+
self[:last_synced]
|
98
123
|
end
|
99
124
|
|
100
|
-
def
|
101
|
-
|
125
|
+
def filtering
|
126
|
+
# assume for now, that first object in filtering array or a filter object itself is the only filtering object
|
127
|
+
filtering = @elasticsearch_response.dig(:_source, :filtering)
|
128
|
+
|
129
|
+
Utility::Filtering.extract_filter(filtering)
|
102
130
|
end
|
103
131
|
|
104
|
-
def
|
105
|
-
Utility::Common.return_if_present(@elasticsearch_response.dig(:_source, :pipeline, :
|
132
|
+
def request_pipeline
|
133
|
+
Utility::Common.return_if_present(@elasticsearch_response.dig(:_source, :pipeline, :name), @connectors_meta.dig(:pipeline, :default_name), DEFAULT_REQUEST_PIPELINE)
|
106
134
|
end
|
107
135
|
|
108
136
|
def formatted
|
@@ -130,19 +158,23 @@ module Core
|
|
130
158
|
end
|
131
159
|
|
132
160
|
def update_last_sync!(job)
|
161
|
+
# if job is nil, connector still needs to be updated, to avoid it stuck at in_progress
|
162
|
+
job_status = job&.status || Connectors::SyncStatus::ERROR
|
163
|
+
job_error = job.nil? ? 'Could\'t find the job' : job.error
|
164
|
+
job_error ||= 'unknown error' if job_status == Connectors::SyncStatus::ERROR
|
165
|
+
connector_status = (job_status == Connectors::SyncStatus::ERROR ? Connectors::ConnectorStatus::ERROR : Connectors::ConnectorStatus::CONNECTED)
|
133
166
|
doc = {
|
134
|
-
:last_sync_status =>
|
167
|
+
:last_sync_status => job_status,
|
135
168
|
:last_synced => Time.now,
|
136
|
-
:last_sync_error =>
|
137
|
-
:
|
169
|
+
:last_sync_error => job_error,
|
170
|
+
:status => connector_status,
|
171
|
+
:error => job_error
|
138
172
|
}
|
139
|
-
|
140
|
-
if job.terminated?
|
173
|
+
if job&.terminated?
|
141
174
|
doc[:last_indexed_document_count] = job[:indexed_document_count]
|
142
175
|
doc[:last_deleted_document_count] = job[:deleted_document_count]
|
143
176
|
end
|
144
|
-
|
145
|
-
Core::ElasticConnectorActions.update_connector_fields(job.connector_id, doc)
|
177
|
+
Core::ElasticConnectorActions.update_connector_fields(id, doc)
|
146
178
|
end
|
147
179
|
|
148
180
|
private
|
@@ -8,6 +8,7 @@
|
|
8
8
|
#
|
9
9
|
require 'active_support/core_ext/hash'
|
10
10
|
require 'connectors/connector_status'
|
11
|
+
require 'connectors/job_trigger_method'
|
11
12
|
require 'connectors/sync_status'
|
12
13
|
require 'utility'
|
13
14
|
require 'elastic-transport'
|
@@ -60,9 +61,14 @@ module Core
|
|
60
61
|
|
61
62
|
def connectors_meta
|
62
63
|
# TODO: remove the usage of with_indifferent_access. Ideally this should return a hash or nil if not found
|
63
|
-
alias_mappings = client.indices.get_mapping(:index => Utility::Constants::CONNECTORS_INDEX).with_indifferent_access
|
64
|
+
alias_mappings = client.indices.get_mapping(:index => Utility::Constants::CONNECTORS_INDEX, :ignore => 404).with_indifferent_access
|
64
65
|
index = get_latest_index_in_alias(Utility::Constants::CONNECTORS_INDEX, alias_mappings.keys)
|
65
|
-
alias_mappings.dig(index, 'mappings', '_meta') || {
|
66
|
+
alias_mappings.dig(index, 'mappings', '_meta') || {
|
67
|
+
:extract_binary_content => true,
|
68
|
+
:name => 'ent-search-generic-ingestion',
|
69
|
+
:reduce_whitespace => true,
|
70
|
+
:run_ml_inference => false,
|
71
|
+
}
|
66
72
|
end
|
67
73
|
|
68
74
|
def search_connectors(query, page_size, offset)
|
@@ -91,6 +97,17 @@ module Core
|
|
91
97
|
)
|
92
98
|
end
|
93
99
|
|
100
|
+
def delete_jobs_by_query(query)
|
101
|
+
client.delete_by_query(
|
102
|
+
:index => Utility::Constants::JOB_INDEX,
|
103
|
+
:body => { :query => query }
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
def delete_indices(indices)
|
108
|
+
client.indices.delete(:index => indices, :ignore_unavailable => true)
|
109
|
+
end
|
110
|
+
|
94
111
|
def update_connector_configuration(connector_id, configuration)
|
95
112
|
update_connector_fields(connector_id, :configuration => configuration)
|
96
113
|
end
|
@@ -145,12 +162,37 @@ module Core
|
|
145
162
|
)
|
146
163
|
end
|
147
164
|
|
148
|
-
def
|
165
|
+
def update_connector_sync_start(connector_id)
|
149
166
|
doc = connector_with_concurrency_control(connector_id)
|
150
167
|
|
168
|
+
body = {
|
169
|
+
last_sync_status: Connectors::SyncStatus::IN_PROGRESS,
|
170
|
+
last_sync_error: nil,
|
171
|
+
status: Connectors::ConnectorStatus::CONNECTED
|
172
|
+
}
|
173
|
+
|
151
174
|
update_connector_fields(
|
152
175
|
connector_id,
|
153
|
-
|
176
|
+
body,
|
177
|
+
doc[:seq_no],
|
178
|
+
doc[:primary_term]
|
179
|
+
)
|
180
|
+
end
|
181
|
+
|
182
|
+
def update_connector_custom_scheduling_last_synced(connector_id, schedule_key)
|
183
|
+
doc = connector_with_concurrency_control(connector_id)
|
184
|
+
|
185
|
+
body = {
|
186
|
+
:custom_scheduling => {
|
187
|
+
schedule_key => {
|
188
|
+
:last_synced => Time.now
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|
192
|
+
|
193
|
+
update_connector_fields(
|
194
|
+
connector_id,
|
195
|
+
body,
|
154
196
|
doc[:seq_no],
|
155
197
|
doc[:primary_term]
|
156
198
|
)
|
@@ -178,13 +220,15 @@ module Core
|
|
178
220
|
status: Connectors::SyncStatus::PENDING,
|
179
221
|
created_at: Time.now,
|
180
222
|
last_seen: Time.now,
|
223
|
+
trigger_method: connector_settings.sync_now? ? Connectors::JobTriggerMethod::ON_DEMAND : Connectors::JobTriggerMethod::SCHEDULED,
|
181
224
|
connector: {
|
182
225
|
id: connector_settings.id,
|
183
226
|
filtering: convert_connector_filtering_to_job_filtering(connector_settings.filtering),
|
184
227
|
index_name: connector_settings.index_name,
|
185
228
|
language: connector_settings[:language],
|
186
229
|
pipeline: connector_settings[:pipeline],
|
187
|
-
service_type: connector_settings.service_type
|
230
|
+
service_type: connector_settings.service_type,
|
231
|
+
configuration: connector_settings.configuration
|
188
232
|
}
|
189
233
|
}
|
190
234
|
|
@@ -220,37 +264,6 @@ module Core
|
|
220
264
|
update_connector_fields(connector_id, body)
|
221
265
|
end
|
222
266
|
|
223
|
-
def update_sync(job_id, metadata)
|
224
|
-
body = {
|
225
|
-
:doc => { :last_seen => Time.now }.merge(metadata)
|
226
|
-
}
|
227
|
-
client.update(:index => Utility::Constants::JOB_INDEX, :id => job_id, :body => body)
|
228
|
-
end
|
229
|
-
|
230
|
-
def complete_sync(connector_id, job_id, metadata, error)
|
231
|
-
sync_status = error ? Connectors::SyncStatus::ERROR : Connectors::SyncStatus::COMPLETED
|
232
|
-
|
233
|
-
metadata ||= {}
|
234
|
-
|
235
|
-
update_connector_fields(connector_id,
|
236
|
-
:last_sync_status => sync_status,
|
237
|
-
:last_sync_error => error,
|
238
|
-
:error => error,
|
239
|
-
:last_synced => Time.now,
|
240
|
-
:last_indexed_document_count => metadata[:indexed_document_count],
|
241
|
-
:last_deleted_document_count => metadata[:deleted_document_count])
|
242
|
-
|
243
|
-
body = {
|
244
|
-
:doc => {
|
245
|
-
:status => sync_status,
|
246
|
-
:completed_at => Time.now,
|
247
|
-
:last_seen => Time.now,
|
248
|
-
:error => error
|
249
|
-
}.merge(metadata)
|
250
|
-
}
|
251
|
-
client.update(:index => Utility::Constants::JOB_INDEX, :id => job_id, :body => body)
|
252
|
-
end
|
253
|
-
|
254
267
|
def fetch_document_ids(index_name)
|
255
268
|
page_size = 1000
|
256
269
|
result = []
|
@@ -331,9 +344,11 @@ module Core
|
|
331
344
|
# Creation of connector index should be handled by Kibana, this method is only used by ftest.rb
|
332
345
|
def ensure_connectors_index_exists
|
333
346
|
mappings = {
|
347
|
+
:dynamic => false,
|
334
348
|
:properties => {
|
335
349
|
:api_key_id => { :type => :keyword },
|
336
350
|
:configuration => { :type => :object },
|
351
|
+
:custom_schedule => { :type => :object },
|
337
352
|
:description => { :type => :text },
|
338
353
|
:error => { :type => :keyword },
|
339
354
|
:features => {
|
@@ -451,6 +466,7 @@ module Core
|
|
451
466
|
# Creation of job index should be handled by Kibana, this method is only used by ftest.rb
|
452
467
|
def ensure_job_index_exists
|
453
468
|
mappings = {
|
469
|
+
:dynamic => false,
|
454
470
|
:properties => {
|
455
471
|
:cancelation_requested_at => { :type => :date },
|
456
472
|
:canceled_at => { :type => :date },
|
@@ -528,8 +544,8 @@ module Core
|
|
528
544
|
end
|
529
545
|
|
530
546
|
def document_count(index_name)
|
531
|
-
client.indices.refresh(:index => index_name)
|
532
|
-
client.count(:index => index_name)['count']
|
547
|
+
client.indices.refresh(:index => index_name, :ignore_unavailable => true)
|
548
|
+
client.count(:index => index_name, :ignore_unavailable => true)['count']
|
533
549
|
end
|
534
550
|
|
535
551
|
private
|
@@ -0,0 +1,32 @@
|
|
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 'core/filtering/advanced_snippet/advanced_snippet_validator'
|
11
|
+
require 'core/filtering/validation_status'
|
12
|
+
require 'core/filtering/hash_against_schema_validator'
|
13
|
+
|
14
|
+
module Core
|
15
|
+
module Filtering
|
16
|
+
module AdvancedSnippet
|
17
|
+
class AdvancedSnippetAgainstSchemaValidator < Core::Filtering::AdvancedSnippet::AdvancedSnippetValidator
|
18
|
+
|
19
|
+
def initialize(advanced_snippet, schema)
|
20
|
+
super(advanced_snippet)
|
21
|
+
@schema = schema
|
22
|
+
@schema_validator = Core::Filtering::SchemaValidator.new(schema: schema, payload: advanced_snippet, error_id: ADVANCED_SNIPPET_ID)
|
23
|
+
end
|
24
|
+
|
25
|
+
def is_snippet_valid
|
26
|
+
@schema_validator.validate_against_schema
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
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 Core
|
11
|
+
module Filtering
|
12
|
+
module AdvancedSnippet
|
13
|
+
class AdvancedSnippetValidator
|
14
|
+
|
15
|
+
ADVANCED_SNIPPET_ID = 'advanced_snippet'
|
16
|
+
|
17
|
+
def initialize(advanced_snippet)
|
18
|
+
@advanced_snippet = advanced_snippet || {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_snippet_valid
|
22
|
+
raise 'Advanced Snippet validation not implemented'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,103 @@
|
|
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 'core/filtering/validation_status'
|
9
|
+
require 'core/filtering/processing_stage'
|
10
|
+
require 'utility/logger'
|
11
|
+
|
12
|
+
module Core
|
13
|
+
module Filtering
|
14
|
+
class FilterValidator
|
15
|
+
|
16
|
+
ADVANCED_SNIPPET = 'Advanced Snippet'
|
17
|
+
SIMPLE_RULES = 'Simple Rules'
|
18
|
+
|
19
|
+
def initialize(snippet_validator_classes: [], rules_validator_classes: [], rules_pre_processing_active: false)
|
20
|
+
@snippet_validators_classes = {
|
21
|
+
'classes' => extract_advanced_snippet_validators(snippet_validator_classes),
|
22
|
+
'type' => ADVANCED_SNIPPET
|
23
|
+
}
|
24
|
+
|
25
|
+
@rules_validator_classes = {
|
26
|
+
'classes' => extract_simple_rule_validators(rules_validator_classes, rules_pre_processing_active),
|
27
|
+
'type' => SIMPLE_RULES
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_filter_valid(filter = {})
|
32
|
+
return { :state => Core::Filtering::ValidationStatus::VALID, :errors => [] } unless filter.present?
|
33
|
+
|
34
|
+
snippet_validation_result = execute_validation(@snippet_validators_classes, filter)
|
35
|
+
log_validation_result(snippet_validation_result, ADVANCED_SNIPPET)
|
36
|
+
|
37
|
+
rules_validation_result = execute_validation(@rules_validator_classes, filter)
|
38
|
+
log_validation_result(rules_validation_result, SIMPLE_RULES)
|
39
|
+
|
40
|
+
merge_validation_results(snippet_validation_result, rules_validation_result)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def log_validation_result(validation_result, validator_type)
|
46
|
+
Utility::Logger.info("Filtering #{validator_type} validation result: #{validation_result[:state]}")
|
47
|
+
if validation_result[:errors].present?
|
48
|
+
validation_result[:errors].each do |error|
|
49
|
+
Utility::Logger.warn("Filtering #{validator_type} validation error for: '#{error[:ids]}': '#{error[:messages]}'")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def merge_validation_results(*validation_results)
|
55
|
+
merged_result = { :state => Core::Filtering::ValidationStatus::VALID, :errors => [] }
|
56
|
+
|
57
|
+
validation_results.each do |validation_result|
|
58
|
+
merged_result[:state] = Core::Filtering::ValidationStatus::INVALID if validation_result[:state] == Core::Filtering::ValidationStatus::INVALID
|
59
|
+
merged_result[:errors].push(*validation_result[:errors]) if validation_result[:errors].present?
|
60
|
+
end
|
61
|
+
|
62
|
+
merged_result
|
63
|
+
end
|
64
|
+
|
65
|
+
def execute_validation(validators, filter)
|
66
|
+
validation_results = []
|
67
|
+
|
68
|
+
validator_type = validators['type']
|
69
|
+
advanced_snippet = filter.dig('advanced_snippet', 'value')
|
70
|
+
|
71
|
+
validators['classes'].each do |validator_class|
|
72
|
+
case validator_type
|
73
|
+
when ADVANCED_SNIPPET
|
74
|
+
validation_result = validator_class.new(advanced_snippet).is_snippet_valid
|
75
|
+
when SIMPLE_RULES
|
76
|
+
validation_result = validator_class.new(filter['rules']).are_rules_valid
|
77
|
+
else
|
78
|
+
raise "Unknown validator: #{validator_type}"
|
79
|
+
end
|
80
|
+
|
81
|
+
validation_results.push(validation_result) if validation_result[:state] == Core::Filtering::ValidationStatus::INVALID
|
82
|
+
end
|
83
|
+
|
84
|
+
merge_validation_results(*validation_results)
|
85
|
+
end
|
86
|
+
|
87
|
+
def extract_advanced_snippet_validators(snippet_validators)
|
88
|
+
snippet_validators.is_a?(Array) ? snippet_validators : [snippet_validators]
|
89
|
+
end
|
90
|
+
|
91
|
+
def extract_simple_rule_validators(rule_validators, pre_processing_active)
|
92
|
+
return rule_validators if rule_validators.is_a?(Array)
|
93
|
+
|
94
|
+
common_validators = rule_validators[Core::Filtering::ProcessingStage::ALL] || []
|
95
|
+
pre_validators = rule_validators[Core::Filtering::ProcessingStage::PRE] || []
|
96
|
+
post_validators = rule_validators[Core::Filtering::ProcessingStage::POST] || []
|
97
|
+
|
98
|
+
# post processing validation will always be executed
|
99
|
+
pre_processing_active ? common_validators + pre_validators + post_validators : common_validators + post_validators
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|