connectors_service 8.6.0.4 → 8.7.0.0.pre.20221117T004928Z

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.
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