connectors_service 8.6.0.4 → 8.7.0.0.pre.20221117T004928Z

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/config/connectors.yml +9 -8
  3. data/lib/app/app.rb +4 -0
  4. data/lib/app/config.rb +3 -0
  5. data/lib/app/dispatcher.rb +44 -17
  6. data/lib/app/preflight_check.rb +11 -0
  7. data/lib/connectors/base/advanced_snippet_against_schema_validator.rb +173 -0
  8. data/lib/connectors/base/advanced_snippet_validator.rb +34 -0
  9. data/lib/connectors/base/connector.rb +43 -14
  10. data/lib/connectors/base/simple_rules_parser.rb +42 -0
  11. data/lib/connectors/example/connector.rb +6 -0
  12. data/lib/connectors/example/example_advanced_snippet_validator.rb +35 -0
  13. data/lib/connectors/gitlab/connector.rb +6 -1
  14. data/lib/connectors/gitlab/gitlab_advanced_snippet_validator.rb +35 -0
  15. data/lib/connectors/mongodb/connector.rb +47 -43
  16. data/lib/connectors/mongodb/mongo_advanced_snippet_against_schema_validator.rb +22 -0
  17. data/lib/connectors/mongodb/mongo_advanced_snippet_schema.rb +292 -0
  18. data/lib/connectors/mongodb/mongo_rules_parser.rb +81 -0
  19. data/lib/connectors/sync_status.rb +6 -1
  20. data/lib/connectors/tolerable_error_helper.rb +43 -0
  21. data/lib/connectors_app/// +13 -0
  22. data/lib/core/configuration.rb +3 -1
  23. data/lib/core/connector_job.rb +210 -0
  24. data/lib/core/connector_settings.rb +52 -16
  25. data/lib/core/elastic_connector_actions.rb +320 -59
  26. data/lib/core/filtering/post_process_engine.rb +39 -0
  27. data/lib/core/filtering/post_process_result.rb +27 -0
  28. data/lib/core/filtering/simple_rule.rb +141 -0
  29. data/lib/core/filtering/validation_job_runner.rb +53 -0
  30. data/lib/core/filtering/validation_status.rb +17 -0
  31. data/lib/core/filtering.rb +17 -0
  32. data/lib/core/ingestion/es_sink.rb +118 -0
  33. data/lib/core/{output_sink.rb → ingestion.rb} +1 -5
  34. data/lib/core/jobs/consumer.rb +132 -0
  35. data/lib/core/jobs/producer.rb +26 -0
  36. data/lib/core/scheduler.rb +40 -10
  37. data/lib/core/single_scheduler.rb +1 -1
  38. data/lib/core/sync_job_runner.rb +80 -16
  39. data/lib/core.rb +4 -0
  40. data/lib/utility/bulk_queue.rb +87 -0
  41. data/lib/utility/constants.rb +7 -0
  42. data/lib/utility/error_monitor.rb +108 -0
  43. data/lib/utility/errors.rb +0 -12
  44. data/lib/utility/filtering.rb +22 -0
  45. data/lib/utility/logger.rb +1 -1
  46. data/lib/utility.rb +11 -4
  47. metadata +31 -12
  48. data/lib/core/output_sink/base_sink.rb +0 -33
  49. data/lib/core/output_sink/combined_sink.rb +0 -38
  50. data/lib/core/output_sink/console_sink.rb +0 -51
  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: 43cb60284b3e1c74a6f34612c0f7509cb4be0375d5871c6b59d7338495c88236
4
- data.tar.gz: 3f176c8770fc9b450a244b5f8cc7a5857668a452c17e19a07ddb412dcb8d2452
3
+ metadata.gz: 39e0bb6ae283bcb1afebd57e715342b7e30a7178c33653dfd05fee6f322061e6
4
+ data.tar.gz: e1fd33b8d28d7d906b4b8eea672a34c0be12e0a124977bbeb10c62c147690a8b
5
5
  SHA512:
6
- metadata.gz: 59746d5ae600b0a77aaede033f5b371cd922e3a4e95f61afda146f5b6e0962026c02c987fc43c203c5274381539f5c76e5ef895fce593ef1883fa6cf620a1702
7
- data.tar.gz: ee799a965b0f5b028c2a41003069d3b7b1e49cabe38453629af8a382ed7ac92b76fe835986c7e5157c1a06b1c2ebae84c20606319bbe7b0aad9a771b12a3f977
6
+ metadata.gz: bb6b96ac0c07e9ed4cbe87f16a17844771c12d7a7ccbb2e8c7aeb571cb146f67e5a0489b1e73ff907613dcda442912ba09c193e0e8e4e11fec553ebe9afdec12
7
+ data.tar.gz: c3c2fc12ac9de6ed37a85dbfb46725e2de37c08beba453c3e9d8a3f0c6c56a078a1c5987931251ca84f18e5a9388b6698ff43abe3ce547d291537b3040b15b0a
@@ -1,10 +1,11 @@
1
1
  # general metadata
2
- version: 8.6.0.4-20221104T200420Z
3
- repository: git@github.com:elastic/ent-search-connectors.git
4
- revision: 2051b3907639a1fe2ae68efdc33c06cf12d38383
2
+ version: 8.7.0.0-20221117T004928Z
3
+ repository: https://github.com/elastic/connectors-ruby.git
4
+ revision: 294214a26b0fe9a4347763b01de681c336e8daae
5
5
  elasticsearch:
6
+ cloud_id: CHANGEME
6
7
  hosts: http://localhost:9200
7
- api_key: OW1FalJJUUI1clBtUVh5RVo1QmU6QVp5LV9pU3RRUXFYb2VVYnlCRWNZdw==
8
+ api_key: CHANGEME
8
9
  retry_on_failure: 3
9
10
  request_timeout: 120
10
11
  disable_warnings: true
@@ -15,10 +16,10 @@ thread_pool:
15
16
  max_threads: 5
16
17
  max_queue: 100
17
18
  log_level: info
18
- ecs_logging: false
19
+ ecs_logging: true
19
20
  poll_interval: 3
20
21
  termination_timeout: 60
21
22
  heartbeat_interval: 1800
22
- native_mode: false
23
- connector_id: 9WEjRIQB5rPmQXyEWJB2
24
- service_type: example
23
+ native_mode: true
24
+ connector_id: CHANGEME
25
+ service_type: CHANGEME
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/config.rb CHANGED
@@ -54,6 +54,9 @@ puts "Parsing #{CONFIG_FILE} configuration file."
54
54
  optional(:poll_interval).value(:integer)
55
55
  optional(:termination_timeout).value(:integer)
56
56
  optional(:heartbeat_interval).value(:integer)
57
+
58
+ optional(:max_ingestion_queue_size).value(:integer) # items
59
+ optional(:max_ingestion_queue_bytes).value(:integer) # bytes
57
60
  end
58
61
  end
59
62
 
@@ -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
- start_sync_task(connector_settings)
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,38 @@ 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
+ max_ingestion_queue_size: (App::Config.max_ingestion_queue_size || Utility::Constants::DEFAULT_MAX_INGESTION_QUEUE_SIZE).to_i,
137
+ max_ingestion_queue_bytes: (App::Config.max_ingestion_queue_bytes || Utility::Constants::DEFAULT_MAX_INGESTION_QUEUE_BYTES).to_i,
138
+ scheduler: scheduler
139
+ )
140
+
141
+ @consumer.subscribe!(index_name: Utility::Constants::JOB_INDEX)
142
+ end
143
+
144
+ def stop_consumer!
145
+ return if @consumer.nil?
146
+ return unless @consumer.running?
147
+
148
+ @consumer.shutdown!
149
+ end
123
150
  end
124
151
  end
125
152
  end
@@ -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 'core/output_sink'
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 'app/config'
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
- filter = get_filter(@job_description[:filtering])
71
+ filtering = Utility::Filtering.extract_filter(@job_description.dig(:connector, :filtering))
42
72
 
43
- @rules = Utility::Common.return_if_present(filter[:rules], [])
44
- @advanced_filter_config = Utility::Common.return_if_present(filter[:advanced_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
- private
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 'core/output_sink'
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