connectors_service 8.5.0.1

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 (67) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +93 -0
  3. data/NOTICE.txt +2 -0
  4. data/bin/connectors_service +4 -0
  5. data/bin/list_connectors +4 -0
  6. data/config/connectors.yml +25 -0
  7. data/lib/app/app.rb +25 -0
  8. data/lib/app/config.rb +132 -0
  9. data/lib/app/console_app.rb +278 -0
  10. data/lib/app/dispatcher.rb +121 -0
  11. data/lib/app/menu.rb +104 -0
  12. data/lib/app/preflight_check.rb +134 -0
  13. data/lib/app/version.rb +10 -0
  14. data/lib/connectors/base/adapter.rb +119 -0
  15. data/lib/connectors/base/connector.rb +57 -0
  16. data/lib/connectors/base/custom_client.rb +111 -0
  17. data/lib/connectors/connector_status.rb +31 -0
  18. data/lib/connectors/crawler/scheduler.rb +32 -0
  19. data/lib/connectors/example/connector.rb +57 -0
  20. data/lib/connectors/example/example_attachments/first_attachment.txt +1 -0
  21. data/lib/connectors/example/example_attachments/second_attachment.txt +1 -0
  22. data/lib/connectors/example/example_attachments/third_attachment.txt +1 -0
  23. data/lib/connectors/gitlab/adapter.rb +50 -0
  24. data/lib/connectors/gitlab/connector.rb +67 -0
  25. data/lib/connectors/gitlab/custom_client.rb +44 -0
  26. data/lib/connectors/gitlab/extractor.rb +69 -0
  27. data/lib/connectors/mongodb/connector.rb +138 -0
  28. data/lib/connectors/registry.rb +52 -0
  29. data/lib/connectors/sync_status.rb +21 -0
  30. data/lib/connectors.rb +16 -0
  31. data/lib/connectors_app/// +13 -0
  32. data/lib/connectors_service.rb +24 -0
  33. data/lib/connectors_utility.rb +16 -0
  34. data/lib/core/configuration.rb +48 -0
  35. data/lib/core/connector_settings.rb +142 -0
  36. data/lib/core/elastic_connector_actions.rb +269 -0
  37. data/lib/core/heartbeat.rb +32 -0
  38. data/lib/core/native_scheduler.rb +24 -0
  39. data/lib/core/output_sink/base_sink.rb +33 -0
  40. data/lib/core/output_sink/combined_sink.rb +38 -0
  41. data/lib/core/output_sink/console_sink.rb +51 -0
  42. data/lib/core/output_sink/es_sink.rb +74 -0
  43. data/lib/core/output_sink.rb +13 -0
  44. data/lib/core/scheduler.rb +158 -0
  45. data/lib/core/single_scheduler.rb +29 -0
  46. data/lib/core/sync_job_runner.rb +111 -0
  47. data/lib/core.rb +16 -0
  48. data/lib/list_connectors.rb +22 -0
  49. data/lib/stubs/app_config.rb +35 -0
  50. data/lib/stubs/connectors/stats.rb +35 -0
  51. data/lib/stubs/service_type.rb +13 -0
  52. data/lib/utility/constants.rb +20 -0
  53. data/lib/utility/cron.rb +81 -0
  54. data/lib/utility/elasticsearch/index/language_data.yml +111 -0
  55. data/lib/utility/elasticsearch/index/mappings.rb +104 -0
  56. data/lib/utility/elasticsearch/index/text_analysis_settings.rb +226 -0
  57. data/lib/utility/environment.rb +33 -0
  58. data/lib/utility/errors.rb +132 -0
  59. data/lib/utility/es_client.rb +84 -0
  60. data/lib/utility/exception_tracking.rb +64 -0
  61. data/lib/utility/extension_mapping_util.rb +123 -0
  62. data/lib/utility/logger.rb +84 -0
  63. data/lib/utility/middleware/basic_auth.rb +27 -0
  64. data/lib/utility/middleware/bearer_auth.rb +27 -0
  65. data/lib/utility/middleware/restrict_hostnames.rb +73 -0
  66. data/lib/utility.rb +16 -0
  67. metadata +487 -0
@@ -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
+
7
+ # frozen_string_literal: true
8
+
9
+ require 'core/scheduler'
10
+ require 'core/connector_settings'
11
+ require 'core/elastic_connector_actions'
12
+ require 'utility/logger'
13
+ require 'utility/exception_tracking'
14
+
15
+ module Connectors
16
+ module Crawler
17
+ class Scheduler < Core::Scheduler
18
+ def connector_settings
19
+ Core::ConnectorSettings.fetch_crawler_connectors || []
20
+ rescue StandardError => e
21
+ Utility::ExceptionTracking.log_exception(e, 'Could not retrieve Crawler connectors due to unexpected error.')
22
+ []
23
+ end
24
+
25
+ private
26
+
27
+ def connector_registered?(service_type)
28
+ service_type == 'elastic-crawler'
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,57 @@
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/connector'
10
+ require 'utility'
11
+
12
+ module Connectors
13
+ module Example
14
+ class Connector < Connectors::Base::Connector
15
+ def self.service_type
16
+ 'example'
17
+ end
18
+
19
+ def self.display_name
20
+ 'Example Connector'
21
+ end
22
+
23
+ def self.configurable_fields
24
+ {
25
+ 'foo' => {
26
+ 'label' => 'Foo',
27
+ 'value' => nil
28
+ }
29
+ }
30
+ end
31
+
32
+ def initialize(configuration: {})
33
+ super
34
+ end
35
+
36
+ def do_health_check
37
+ # Do the health check by trying to access 3rd-party system just to verify that everything is set up properly.
38
+ #
39
+ # To emulate unhealthy 3rd-party system situation, uncomment the following line:
40
+ # raise 'something went wrong'
41
+ end
42
+
43
+ def yield_documents
44
+ attachments = [
45
+ File.open('./lib/connectors/example/example_attachments/first_attachment.txt'),
46
+ File.open('./lib/connectors/example/example_attachments/second_attachment.txt'),
47
+ File.open('./lib/connectors/example/example_attachments/third_attachment.txt')
48
+ ]
49
+
50
+ attachments.each_with_index do |att, index|
51
+ data = { id: (index + 1).to_s, name: "example document #{index + 1}", _attachment: File.read(att) }
52
+ yield data
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1 @@
1
+ e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYyNjM5Clxjb2NvYXRleHRzY2FsaW5nMFxjb2NvYXBsYXRmb3JtMHtcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYS1Cb2xkO1xmMVxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYTtcZjJcZnN3aXNzXGZjaGFyc2V0MCBIZWx2ZXRpY2EtT2JsaXF1ZTsKfQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9CntcKlxleHBhbmRlZGNvbG9ydGJsOzt9ClxwYXBlcncxMTkwMFxwYXBlcmgxNjg0MFxtYXJnbDE0NDBcbWFyZ3IxNDQwXHZpZXd3MTE1MjBcdmlld2g4NDAwXHZpZXdraW5kMApccGFyZFx0eDU2Nlx0eDExMzNcdHgxNzAwXHR4MjI2N1x0eDI4MzRcdHgzNDAxXHR4Mzk2OFx0eDQ1MzVcdHg1MTAyXHR4NTY2OVx0eDYyMzZcdHg2ODAzXHBhcmRpcm5hdHVyYWxccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMjQgXGNmMCBFeGFtcGxlIEF0dGFjaG1lbnQgMDFcClwKClxmMVxiMCBUaGlzIGlzIHRoZQpcZjJcaSAgZmlyc3QgClxmMVxpMCBvZiBcdWwgdGhyZWUgXHVsbm9uZSBleGFtcGxlIGF0dGFjaG1lbnRzfQ==
@@ -0,0 +1 @@
1
+ e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYyNjM5Clxjb2NvYXRleHRzY2FsaW5nMFxjb2NvYXBsYXRmb3JtMHtcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYS1Cb2xkO1xmMVxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYTtcZjJcZnN3aXNzXGZjaGFyc2V0MCBIZWx2ZXRpY2EtT2JsaXF1ZTsKfQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9CntcKlxleHBhbmRlZGNvbG9ydGJsOzt9ClxwYXBlcncxMTkwMFxwYXBlcmgxNjg0MFxtYXJnbDE0NDBcbWFyZ3IxNDQwXHZpZXd3MTE1MjBcdmlld2g4NDAwXHZpZXdraW5kMApccGFyZFx0eDU2Nlx0eDExMzNcdHgxNzAwXHR4MjI2N1x0eDI4MzRcdHgzNDAxXHR4Mzk2OFx0eDQ1MzVcdHg1MTAyXHR4NTY2OVx0eDYyMzZcdHg2ODAzXHBhcmRpcm5hdHVyYWxccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMjQgXGNmMCBFeGFtcGxlIEF0dGFjaG1lbnQgMDJcClwKClxmMVxiMCBUaGlzIGlzIHRoZQpcZjJcaSAgc2Vjb25kIApcZjFcaTAgb2YgXHVsIHRocmVlIFx1bG5vbmUgZXhhbXBsZSBhdHRhY2htZW50c30=
@@ -0,0 +1 @@
1
+ e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYyNjM5Clxjb2NvYXRleHRzY2FsaW5nMFxjb2NvYXBsYXRmb3JtMHtcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYS1Cb2xkO1xmMVxmc3dpc3NcZmNoYXJzZXQwIEhlbHZldGljYTtcZjJcZnN3aXNzXGZjaGFyc2V0MCBIZWx2ZXRpY2EtT2JsaXF1ZTsKfQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTt9CntcKlxleHBhbmRlZGNvbG9ydGJsOzt9ClxwYXBlcncxMTkwMFxwYXBlcmgxNjg0MFxtYXJnbDE0NDBcbWFyZ3IxNDQwXHZpZXd3MTE1MjBcdmlld2g4NDAwXHZpZXdraW5kMApccGFyZFx0eDU2Nlx0eDExMzNcdHgxNzAwXHR4MjI2N1x0eDI4MzRcdHgzNDAxXHR4Mzk2OFx0eDQ1MzVcdHg1MTAyXHR4NTY2OVx0eDYyMzZcdHg2ODAzXHBhcmRpcm5hdHVyYWxccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMjQgXGNmMCBFeGFtcGxlIEF0dGFjaG1lbnQgMDNcClwKClxmMVxiMCBUaGlzIGlzIHRoZQpcZjJcaSAgdGhpcmQgClxmMVxpMCBvZiBcdWwgdGhyZWUgXHVsbm9uZSBleGFtcGxlIGF0dGFjaG1lbnRzfQ==
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
5
+ # or more contributor license agreements. Licensed under the Elastic License;
6
+ # you may not use this file except in compliance with the Elastic License.
7
+ #
8
+ require 'hashie/mash'
9
+ require 'active_support/core_ext/hash/indifferent_access'
10
+ require 'connectors/base/adapter'
11
+
12
+ module Connectors
13
+ module GitLab
14
+ class Adapter < Connectors::Base::Adapter
15
+ # it's important to have this to generate ID converters between the GitLab ID and the
16
+ # Enterprise Search document ID. The Enterprise Search document ID will be prefixed with the service type,
17
+ # in our case - `gitlab`.
18
+ generate_id_helpers :gitlab, 'gitlab'
19
+
20
+ def self.to_es_document(type, source_doc)
21
+ source_doc = source_doc.with_indifferent_access
22
+ result = {}
23
+ case type.to_sym
24
+ when :project
25
+ result.merge!(
26
+ {
27
+ :url => source_doc[:web_url],
28
+ :body => source_doc[:description],
29
+ :title => source_doc[:name],
30
+ :created_at => source_doc[:created_at],
31
+ :last_modified_at => source_doc[:last_activity_at],
32
+ :visibility => source_doc[:visibility],
33
+ :namespace => if source_doc[:namespace].nil?
34
+ nil
35
+ else
36
+ source_doc[:namespace][:name]
37
+ end
38
+ }
39
+ )
40
+ else
41
+ # don't remap
42
+ result.merge!(source_doc)
43
+ end
44
+ result[:id] = gitlab_id_to_es_id(source_doc[:id])
45
+ result[:type] = type
46
+ result
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,67 @@
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
+ require 'active_support/core_ext/hash/indifferent_access'
9
+
10
+ require 'connectors/base/connector'
11
+ require 'connectors/gitlab/extractor'
12
+ require 'connectors/gitlab/custom_client'
13
+ require 'connectors/gitlab/adapter'
14
+ require 'core/output_sink'
15
+
16
+ module Connectors
17
+ module GitLab
18
+ class Connector < Connectors::Base::Connector
19
+ def self.service_type
20
+ 'gitlab'
21
+ end
22
+
23
+ def self.display_name
24
+ 'GitLab Connector'
25
+ end
26
+
27
+ def self.configurable_fields
28
+ {
29
+ :base_url => {
30
+ :label => 'Base URL',
31
+ :value => Connectors::GitLab::DEFAULT_BASE_URL
32
+ },
33
+ :api_key => {
34
+ :label => 'API Key'
35
+ }
36
+ }
37
+ end
38
+
39
+ def initialize(configuration: {})
40
+ super
41
+
42
+ @extractor = Connectors::GitLab::Extractor.new(
43
+ :base_url => configuration.dig(:base_url, :value),
44
+ :api_token => configuration.dig(:api_token, :value)
45
+ )
46
+ end
47
+
48
+ def yield_documents
49
+ next_page_link = nil
50
+ loop do
51
+ next_page_link = @extractor.yield_projects_page(next_page_link) do |projects_chunk|
52
+ projects_chunk.each do |project|
53
+ yield Connectors::GitLab::Adapter.to_es_document(:project, project)
54
+ end
55
+ end
56
+ break unless next_page_link.present?
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def do_health_check
63
+ @extractor.health_check
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,44 @@
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
+ require 'faraday_middleware/response/follow_redirects'
7
+ require 'connectors/base/custom_client'
8
+ require 'utility/middleware/bearer_auth'
9
+ require 'utility/middleware/basic_auth'
10
+ require 'utility/middleware/restrict_hostnames'
11
+
12
+ require 'app/config'
13
+
14
+ module Connectors
15
+ module GitLab
16
+ DEFAULT_BASE_URL = 'https://gitlab.com/api/v4'
17
+
18
+ class CustomClient < Connectors::Base::CustomClient
19
+ attr_reader :api_token
20
+
21
+ class ClientError < StandardError
22
+ attr_reader :status_code, :endpoint, :api_token
23
+
24
+ def initialize(status_code, endpoint)
25
+ @status_code = status_code
26
+ @endpoint = endpoint
27
+ end
28
+ end
29
+
30
+ def initialize(base_url:, api_token:, ensure_fresh_auth: nil)
31
+ @api_token = api_token
32
+ super(:base_url => base_url || DEFAULT_BASE_URL, :ensure_fresh_auth => ensure_fresh_auth)
33
+ end
34
+
35
+ def additional_middleware
36
+ [
37
+ ::FaradayMiddleware::FollowRedirects,
38
+ [Utility::Middleware::RestrictHostnames, { :allowed_hosts => [base_url, DEFAULT_BASE_URL] }],
39
+ [Utility::Middleware::BearerAuth, { :bearer_auth_token => api_token }]
40
+ ]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,69 @@
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 'hashie'
10
+ require 'json'
11
+ require 'rack/utils'
12
+ require 'active_support/core_ext/hash/indifferent_access'
13
+ require 'connectors/gitlab/custom_client'
14
+
15
+ module Connectors
16
+ module GitLab
17
+ class Extractor
18
+ PAGE_SIZE = 100 # max is 100
19
+
20
+ def initialize(base_url: nil, api_token: nil, owned_only: true)
21
+ super()
22
+ @base_url = base_url
23
+ @api_token = api_token
24
+ # only get projects that user owns
25
+ @owned_only = owned_only
26
+ end
27
+
28
+ def yield_projects_page(next_page_link = nil)
29
+ query_params = {
30
+ :pagination => :keyset,
31
+ :per_page => PAGE_SIZE,
32
+ :order_by => :id,
33
+ :sort => :desc,
34
+ :owned => @owned_only
35
+ }
36
+
37
+ if next_page_link.present?
38
+ if (matcher = /(https?:[^>]*)/.match(next_page_link))
39
+ clean_query = URI.parse(matcher.captures[0]).query
40
+ query_params = Rack::Utils.parse_query(clean_query)
41
+ else
42
+ raise "Next page link has unexpected format: #{next_page_link}"
43
+ end
44
+ end
45
+ response = client.get('projects', query_params)
46
+
47
+ projects_chunk = JSON.parse(response.body)
48
+ yield projects_chunk
49
+
50
+ # return next link
51
+ response.headers['Link'] || nil
52
+ end
53
+
54
+ def health_check
55
+ # let's do a simple call to get the current user
56
+ response = client.get('user')
57
+ unless response.present? && response.status == 200
58
+ raise "Health check failed with response status #{response.status} and body #{response.body}"
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def client
65
+ @client ||= Connectors::GitLab::CustomClient.new(base_url: @base_url, api_token: @api_token)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,138 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require 'active_support/core_ext/hash/indifferent_access'
10
+ require 'connectors/base/connector'
11
+ require 'mongo'
12
+
13
+ module Connectors
14
+ module MongoDB
15
+ class Connector < Connectors::Base::Connector
16
+ def self.service_type
17
+ 'mongodb'
18
+ end
19
+
20
+ def self.display_name
21
+ 'MongoDB'
22
+ end
23
+
24
+ def self.configurable_fields
25
+ {
26
+ :host => {
27
+ :label => 'Server Hostname'
28
+ },
29
+ :user => {
30
+ :label => 'Username'
31
+ },
32
+ :password => {
33
+ :label => 'Password'
34
+ },
35
+ :database => {
36
+ :label => 'Database'
37
+ },
38
+ :collection => {
39
+ :label => 'Collection'
40
+ },
41
+ :direct_connection => {
42
+ :label => 'Direct connection? (true/false)'
43
+ }
44
+ }
45
+ end
46
+
47
+ def initialize(configuration: {})
48
+ super
49
+
50
+ @host = configuration.dig(:host, :value)
51
+ @database = configuration.dig(:database, :value)
52
+ @collection = configuration.dig(:collection, :value)
53
+ @user = configuration.dig(:user, :value)
54
+ @password = configuration.dig(:password, :value)
55
+ @direct_connection = configuration.dig(:direct_connection, :value)
56
+ end
57
+
58
+ def yield_documents
59
+ with_client do |client|
60
+ client[@collection].find.each do |document|
61
+ doc = document.with_indifferent_access
62
+
63
+ yield serialize(doc)
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def do_health_check
71
+ with_client do |_client|
72
+ Utility::Logger.debug("Mongo at #{@host}/#{@database} looks healthy.")
73
+ end
74
+ end
75
+
76
+ def with_client
77
+ raise "Invalid value for 'Direct connection' : #{@direct_connection}." unless %w[true false].include?(@direct_connection.to_s.strip.downcase)
78
+
79
+ client = if @user.present? || @password.present?
80
+ Mongo::Client.new(
81
+ @host,
82
+ database: @database,
83
+ direct_connection: to_boolean(@direct_connection),
84
+ user: @user,
85
+ password: @password
86
+ )
87
+ else
88
+ Mongo::Client.new(
89
+ @host,
90
+ database: @database,
91
+ direct_connection: to_boolean(@direct_connection)
92
+ )
93
+ end
94
+
95
+ begin
96
+ Utility::Logger.debug("Existing Databases #{client.database_names}")
97
+ Utility::Logger.debug('Existing Collections:')
98
+
99
+ client.collections.each { |coll| Utility::Logger.debug(coll.name) }
100
+
101
+ yield client
102
+ ensure
103
+ client.close
104
+ end
105
+ end
106
+
107
+ def serialize(mongodb_document)
108
+ # This is some lazy serialization here.
109
+ # Problem: MongoDB has its own format of things - e.g. ids are Bson::ObjectId, which when serialized to JSON
110
+ # will produce something like: 'id': { '$oid': '536268a06d2d7019ba000000' }, which is not good for us
111
+ case mongodb_document
112
+ when BSON::ObjectId
113
+ mongodb_document.to_s
114
+ when BSON::Decimal128
115
+ mongodb_document.to_big_decimal # potential problems with NaNs but also will get treated as a string by Elasticsearch anyway
116
+ when String
117
+ # it's here cause Strings are Arrays too :/
118
+ mongodb_document.to_s
119
+ when Array
120
+ mongodb_document.map { |v| serialize(v) }
121
+ when Hash
122
+ mongodb_document.map do |key, value|
123
+ remapped_key = key == '_id' ? 'id' : key
124
+
125
+ remapped_value = serialize(value)
126
+ [remapped_key, remapped_value]
127
+ end.to_h
128
+ else
129
+ mongodb_document
130
+ end
131
+ end
132
+
133
+ def to_boolean(value)
134
+ value == true || value =~ (/(true|t|yes|y|1)$/i)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,52 @@
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
+ module Connectors
8
+ class Factory
9
+ attr_reader :connectors
10
+
11
+ def initialize
12
+ @connectors = {}
13
+ end
14
+
15
+ def register(name, klass)
16
+ @connectors[name] = klass
17
+ end
18
+
19
+ def registered?(name)
20
+ @connectors.has_key?(name)
21
+ end
22
+
23
+ def connector_class(name)
24
+ @connectors[name]
25
+ end
26
+
27
+ def connector(name, configuration)
28
+ klass = connector_class(name)
29
+ if klass.present?
30
+ return klass.new(configuration: configuration)
31
+ end
32
+ raise "Connector #{name} is not yet registered. You need to register it before use"
33
+ end
34
+
35
+ def registered_connectors
36
+ @connectors.keys.sort
37
+ end
38
+ end
39
+
40
+ REGISTRY = Factory.new
41
+
42
+ require_relative './example/connector'
43
+ REGISTRY.register(Connectors::Example::Connector.service_type, Connectors::Example::Connector)
44
+
45
+ # loading plugins (might replace this with a directory scan and conventions on names)
46
+ require_relative './gitlab/connector'
47
+
48
+ REGISTRY.register(Connectors::GitLab::Connector.service_type, Connectors::GitLab::Connector)
49
+
50
+ require_relative 'mongodb/connector'
51
+ REGISTRY.register(Connectors::MongoDB::Connector.service_type, Connectors::MongoDB::Connector)
52
+ end
@@ -0,0 +1,21 @@
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
+ module Connectors
10
+ class SyncStatus
11
+ COMPLETED = 'completed'
12
+ IN_PROGRESS = 'in_progress'
13
+ FAILED = 'failed'
14
+
15
+ STATUSES = [
16
+ COMPLETED,
17
+ IN_PROGRESS,
18
+ FAILED
19
+ ]
20
+ end
21
+ end
data/lib/connectors.rb ADDED
@@ -0,0 +1,16 @@
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
+ require 'utility'
8
+
9
+ def required_path(absolute_path)
10
+ absolute_dir = File.dirname(absolute_path)
11
+ relative_dir = absolute_dir.sub(/.*lib\/connectors/, 'connectors')
12
+ name = File.basename(absolute_path, '.rb')
13
+ File.join(relative_dir, name)
14
+ end
15
+
16
+ Dir[File.join(__dir__, 'connectors/**/*.rb')].each { |f| require required_path(f) }
@@ -0,0 +1,13 @@
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
+ module ConnectorsApp
8
+ module Errors
9
+ INVALID_API_KEY = 'INVALID_API_KEY'
10
+ UNSUPPORTED_AUTH_SCHEME = 'UNSUPPORTED_AUTH_SCHEME'
11
+ INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR'
12
+ end
13
+ end
@@ -0,0 +1,24 @@
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 'app/config'
10
+ require 'app/dispatcher'
11
+ require 'app/preflight_check'
12
+ require 'utility'
13
+
14
+ class ConnectorsService
15
+ def self.run!
16
+ Utility::Environment.set_execution_environment(App::Config) do
17
+ App::PreflightCheck.run!
18
+ App::Dispatcher.start!
19
+ rescue App::PreflightCheck::CheckFailure => e
20
+ Utility::Logger.error("Preflight check failed: #{e.message}")
21
+ exit(-1)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
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_relative 'utility'
10
+
11
+ require_relative 'connectors/connector_status'
12
+ require_relative 'connectors/sync_status'
13
+ require_relative 'core/scheduler'
14
+ require_relative 'core/elastic_connector_actions'
15
+
16
+ require_relative 'connectors/crawler/scheduler'
@@ -0,0 +1,48 @@
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/connector_status'
10
+ require 'connectors/registry'
11
+ require 'core/connector_settings'
12
+ require 'core/elastic_connector_actions'
13
+ require 'utility/logger'
14
+
15
+ module Core
16
+ class Configuration
17
+ class << self
18
+
19
+ def update(connector_settings, service_type = nil)
20
+ if connector_settings.connector_status == Connectors::ConnectorStatus::CREATED
21
+ connector_class = Connectors::REGISTRY.connector_class(connector_settings.service_type || service_type)
22
+ unless connector_class
23
+ Utility::Logger.error("Couldn't find connector for service type #{connector_settings.service_type || service_type}")
24
+ return
25
+ end
26
+ configuration = connector_class.configurable_fields
27
+ doc = {
28
+ :configuration => configuration
29
+ }
30
+
31
+ doc[:service_type] = service_type if service_type && connector_settings.needs_service_type?
32
+
33
+ # We want to set connector to CONFIGURED status if all configurable fields have default values
34
+ new_connector_status = if configuration.values.all? { |setting| setting[:value].present? }
35
+ Utility::Logger.debug("All connector configurable fields provided default values for #{connector_settings.formatted}.")
36
+ Connectors::ConnectorStatus::CONFIGURED
37
+ else
38
+ Connectors::ConnectorStatus::NEEDS_CONFIGURATION
39
+ end
40
+
41
+ doc[:status] = new_connector_status
42
+ Utility::Logger.info("Changing connector status to #{new_connector_status} for #{connector_settings.formatted}.")
43
+ Core::ElasticConnectorActions.update_connector_fields(connector_settings.id, doc)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end