connectors_sdk 8.3.0.0.pre.20220517T144653Z → 8.3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b86ca5e489e3cef3b9f2c04a462baf71d6b43805731b0cb52ba2f56f5078d6d3
4
- data.tar.gz: 044e860f11163e82c63f66276c3d5628b761c5dfcc5168fc8f80b63ca87d19f0
3
+ metadata.gz: 5e3f22d66affcaf3b17f9f7a42b6a782b43b612f7a0b8ee0ab5115f23305ff6d
4
+ data.tar.gz: 8d6a20fbaab9e04c0269f0afe520897be36c07053f0b6b56b257863ef66ad35d
5
5
  SHA512:
6
- metadata.gz: 528fa5260cf80a3ebb918478e1be2e7cac1668588853ad72b1259095086090c15a0cbc028523b1de8b775b3b64ed7427c6fab7ffccc562d559c8164456b84c4b
7
- data.tar.gz: a58d353e2b48ffda33aa287d0fa6bb1400c531aae2794995ff3b644b779426d10bb3efc8735d8e07c353fae64bc0b3b0af58427d989f499210a19244b23c1a35
6
+ metadata.gz: 9c87a941dcbb513f007bba44806a386ec1525cbb28863877fdeb9e57467e7049d05c971db6642cf29fa4e0910c007e12de97ae782f855e84d16d3748af5467a9
7
+ data.tar.gz: 46f89335d22031d52967ccdf5ad3e5b19021df9d689a5206820ae99a81cf99a6689deeafd8456d9072dcde9e597f14259d7cfc5c4f59bcc4075625e0fb33aa91
@@ -9,6 +9,7 @@
9
9
  require 'faraday_middleware'
10
10
 
11
11
  require 'connectors_shared/middleware/bearer_auth'
12
+ require 'connectors_shared/middleware/basic_auth'
12
13
  require 'connectors_shared/middleware/restrict_hostnames'
13
14
  require 'connectors_sdk/base/custom_client'
14
15
 
@@ -30,10 +31,12 @@ module ConnectorsSdk
30
31
 
31
32
  MEDIA_API_BASE_URL = 'https://api.media.atlassian.com'
32
33
 
33
- attr_reader :base_url, :access_token
34
+ attr_reader :base_url, :access_token, :basic_auth_token
34
35
 
35
- def initialize(base_url:, access_token:, ensure_fresh_auth: nil)
36
+ def initialize(base_url:, access_token: nil, basic_auth_token: nil, ensure_fresh_auth: nil)
37
+ raise 'Either access_token or basic_auth_token must be provided' unless access_token.present? || basic_auth_token.present?
36
38
  @access_token = access_token
39
+ @basic_auth_token = basic_auth_token
37
40
  super(:base_url => base_url, :ensure_fresh_auth => ensure_fresh_auth)
38
41
  end
39
42
 
@@ -42,11 +45,17 @@ module ConnectorsSdk
42
45
  end
43
46
 
44
47
  def additional_middleware
45
- [
46
- ::FaradayMiddleware::FollowRedirects,
47
- [ConnectorsShared::Middleware::RestrictHostnames, { :allowed_hosts => [base_url, MEDIA_API_BASE_URL] }],
48
- [ConnectorsShared::Middleware::BearerAuth, { :bearer_auth_token => @access_token }]
48
+ result = [
49
+ FaradayMiddleware::FollowRedirects,
50
+ [ConnectorsShared::Middleware::RestrictHostnames, { :allowed_hosts => [base_url, MEDIA_API_BASE_URL] }]
49
51
  ]
52
+ if @access_token.present?
53
+ result.append([ConnectorsShared::Middleware::BearerAuth, { :bearer_auth_token => @access_token }])
54
+ elsif @basic_auth_token.present?
55
+ result.append([ConnectorsShared::Middleware::BasicAuth, { :basic_auth_token => @basic_auth_token }])
56
+ else
57
+ raise 'Either access token or basic auth must be provided'
58
+ end
50
59
  end
51
60
 
52
61
  def update_auth_data!(new_access_token)
@@ -10,13 +10,25 @@ require 'bson'
10
10
 
11
11
  module ConnectorsSdk
12
12
  module Base
13
- class HttpCallWrapper
13
+ class Connector
14
14
  def extractor(params)
15
+ content_source_id = params.fetch(:content_source_id)
16
+ secret_storage = params[:secret_storage]
17
+
15
18
  extractor_class.new(
16
- content_source_id: params[:content_source_id] || "GENERATED-#{BSON::ObjectId.new}",
19
+ content_source_id: content_source_id || "GENERATED-#{BSON::ObjectId.new}",
17
20
  service_type: service_type,
18
- authorization_data_proc: proc { { access_token: params[:access_token] } },
19
- client_proc: proc { client(params) },
21
+ authorization_data_proc: proc do
22
+ secret = secret_storage.fetch_secret(content_source_id)
23
+ {
24
+ access_token: secret[:access_token]
25
+ }
26
+ end,
27
+ client_proc: proc {
28
+ secret = secret_storage.fetch_secret(content_source_id)
29
+ params[:access_token] = secret[:access_token]
30
+ client(params)
31
+ },
20
32
  config: config(params),
21
33
  features: params.fetch(:features, {}) || {}
22
34
  )
@@ -68,6 +80,10 @@ module ConnectorsSdk
68
80
  end
69
81
  end
70
82
 
83
+ def download(params)
84
+ extractor(params).download(params[:meta])
85
+ end
86
+
71
87
  def authorization_uri(params)
72
88
  authorization.authorization_uri(params)
73
89
  end
@@ -80,18 +96,18 @@ module ConnectorsSdk
80
96
  authorization.refresh(params)
81
97
  end
82
98
 
83
- def download(params)
84
- extractor(params).download(params[:meta])
85
- end
86
-
87
99
  def source_status(params)
88
100
  health_check(params)
89
- { :status => 'OK', :statusCode => 200, :message => "Connected to #{name}" }
101
+ { :status => 'OK', :statusCode => 200, :message => "Connected to #{display_name}" }
90
102
  rescue StandardError => e
91
103
  { :status => 'FAILURE', :statusCode => e.is_a?(custom_client_error) ? e.status_code : 500, :message => e.message }
92
104
  end
93
105
 
94
- def name
106
+ def compare_secrets(*)
107
+ raise 'Not implemented for this connector'
108
+ end
109
+
110
+ def display_name
95
111
  raise 'Not implemented for this connector'
96
112
  end
97
113
 
@@ -99,6 +115,14 @@ module ConnectorsSdk
99
115
  self.class::SERVICE_TYPE
100
116
  end
101
117
 
118
+ def connection_requires_redirect
119
+ false
120
+ end
121
+
122
+ def configurable_fields
123
+ []
124
+ end
125
+
102
126
  private
103
127
 
104
128
  def convert_third_party_errors
@@ -130,6 +154,13 @@ module ConnectorsSdk
130
154
  def health_check(*)
131
155
  raise 'Not implemented for this connector'
132
156
  end
157
+
158
+ def missing_secrets?(params)
159
+ missing = %w[secret other_secret].select { |field| params[field.to_sym].nil? }
160
+ unless missing.blank?
161
+ raise ConnectorsShared::ClientError.new("Missing required fields: #{missing.join(', ')}")
162
+ end
163
+ end
133
164
  end
134
165
  end
135
166
  end
@@ -188,16 +188,19 @@ module ConnectorsSdk
188
188
  end
189
189
 
190
190
  def permissions(source_user_id, &block)
191
+ result = []
191
192
  Connectors::Stats.measure("extractor.#{Connectors::Stats.class_key(self.class)}.permissions") do
192
193
  with_auth_tokens_and_retry do
193
194
  Connectors::Stats.measure("extractor.#{Connectors::Stats.class_key(self.class)}.yield_permissions") do
194
195
  yield_permissions(source_user_id) do |permissions|
195
196
  log_info("Extracted #{permissions.size} permissions for source user #{source_user_id}")
197
+ result = permissions
196
198
  block.call(permissions) if block_given?
197
199
  end
198
200
  end
199
201
  end
200
202
  end
203
+ result.each
201
204
  end
202
205
 
203
206
  ConnectorsShared::Logger::SUPPORTED_LOG_LEVELS.each do |log_level|
@@ -24,11 +24,16 @@ module ConnectorsSdk
24
24
 
25
25
  REGISTRY = Factory.new
26
26
 
27
+ require_relative '../stub_connector/connector'
28
+ REGISTRY.register(ConnectorsSdk::StubConnector::Connector::SERVICE_TYPE, ConnectorsSdk::StubConnector::Connector)
29
+
27
30
  # loading plugins (might replace this with a directory scan and conventions on names)
28
- require_relative '../share_point/http_call_wrapper'
29
- require_relative '../confluence_cloud//http_call_wrapper'
31
+ require_relative '../share_point/connector'
32
+ require_relative '../confluence_cloud/connector'
33
+ require_relative '../gitlab/connector'
30
34
 
31
- REGISTRY.register(ConnectorsSdk::SharePoint::HttpCallWrapper::SERVICE_TYPE, ConnectorsSdk::SharePoint::HttpCallWrapper)
32
- REGISTRY.register(ConnectorsSdk::ConfluenceCloud::HttpCallWrapper::SERVICE_TYPE, ConnectorsSdk::ConfluenceCloud::HttpCallWrapper)
35
+ REGISTRY.register(ConnectorsSdk::SharePoint::Connector::SERVICE_TYPE, ConnectorsSdk::SharePoint::Connector)
36
+ REGISTRY.register(ConnectorsSdk::ConfluenceCloud::Connector::SERVICE_TYPE, ConnectorsSdk::ConfluenceCloud::Connector)
37
+ REGISTRY.register(ConnectorsSdk::GitLab::Connector::SERVICE_TYPE, ConnectorsSdk::GitLab::Connector)
33
38
  end
34
39
  end
@@ -82,7 +82,7 @@ module ConnectorsSdk
82
82
  private
83
83
 
84
84
  def content_base_url
85
- 'https://workplace-search.atlassian.net/wiki'
85
+ config.base_url
86
86
  end
87
87
 
88
88
  def yield_spaces
@@ -0,0 +1,110 @@
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 'base64'
10
+ require 'connectors_sdk/atlassian/config'
11
+ require 'connectors_sdk/confluence_cloud/extractor'
12
+ require 'connectors_sdk/confluence_cloud/authorization'
13
+ require 'connectors_sdk/confluence_cloud/custom_client'
14
+ require 'connectors_sdk/base/connector'
15
+
16
+ module ConnectorsSdk
17
+ module ConfluenceCloud
18
+ class Connector < ConnectorsSdk::Base::Connector
19
+ SERVICE_TYPE = 'confluence_cloud'
20
+
21
+ def compare_secrets(params)
22
+ missing_secrets?(params)
23
+
24
+ {
25
+ :equivalent => params[:secret] == params[:other_secret]
26
+ }
27
+ end
28
+
29
+ def display_name
30
+ 'Confluence Cloud'
31
+ end
32
+
33
+ def configurable_fields
34
+ [
35
+ {
36
+ 'key' => 'base_url',
37
+ 'label' => 'Confluence Cloud Base URL'
38
+ },
39
+ {
40
+ 'key' => 'confluence_user_email',
41
+ 'label' => 'Confluence user email'
42
+ },
43
+ {
44
+ 'key' => 'confluence_api_token',
45
+ 'label' => 'Confluence user REST API Token'
46
+ },
47
+ ]
48
+ end
49
+
50
+ private
51
+
52
+ def extractor_class
53
+ ConnectorsSdk::ConfluenceCloud::Extractor
54
+ end
55
+
56
+ def authorization
57
+ ConnectorsSdk::ConfluenceCloud::Authorization
58
+ end
59
+
60
+ def client(params)
61
+ ConnectorsSdk::ConfluenceCloud::CustomClient.new(
62
+ :base_url => extract_base_url(params),
63
+ :basic_auth_token => extract_basic_auth_token(params)
64
+ )
65
+ end
66
+
67
+ def custom_client_error
68
+ ConnectorsSdk::Atlassian::CustomClient::ClientError
69
+ end
70
+
71
+ def config(params)
72
+ ConnectorsSdk::Atlassian::Config.new(
73
+ :base_url => extract_base_url(params),
74
+ :cursors => params.fetch(:cursors, {}) || {},
75
+ :index_permissions => params[:index_permissions] || false
76
+ )
77
+ end
78
+
79
+ def health_check(params)
80
+ client(params).me
81
+ end
82
+
83
+ def base_url(cloud_id)
84
+ "https://api.atlassian.com/ex/confluence/#{cloud_id}"
85
+ end
86
+
87
+ def is_basic_auth(params)
88
+ login = params.fetch('confluence_user_email', nil)
89
+ api_token = params.fetch('confluence_api_token', nil)
90
+ login.present? && api_token.present?
91
+ end
92
+
93
+ def extract_basic_auth_token(params)
94
+ login = params.fetch('confluence_user_email', nil)
95
+ api_token = params.fetch('confluence_api_token', nil)
96
+ nil unless login.present? && api_token.present?
97
+ Base64.strict_encode64("#{login}:#{api_token}")
98
+ end
99
+
100
+ def extract_base_url(params)
101
+ # From Confluence API documentation:
102
+ # Requests that use OAuth 2.0 (3LO) are made via api.atlassian.com (not https://your-domain.atlassian.net).
103
+ if is_basic_auth(params)
104
+ return params[:base_url].end_with?('/wiki') ? params[:base_url] : "#{params[:base_url]}/wiki"
105
+ end
106
+ base_url(params[:cloud_id])
107
+ end
108
+ end
109
+ end
110
+ end
@@ -52,7 +52,8 @@ module ConnectorsSdk
52
52
  def download(item)
53
53
  content = item[:content]
54
54
  parent_id = content.dig('container', 'id')
55
- client.download("#{client.base_url}/wiki/rest/api/content/#{parent_id}/child/attachment/#{content['id']}/download").body
55
+ base_url = client.base_url.end_with?('/wiki') ? client.base_url : "#{client.base_url}/wiki"
56
+ client.download("#{base_url}/rest/api/content/#{parent_id}/child/attachment/#{content['id']}/download").body
56
57
  end
57
58
  end
58
59
  end
@@ -0,0 +1,42 @@
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 'connectors_sdk/base/adapter'
10
+
11
+ module ConnectorsSdk
12
+ module GitLab
13
+ class Adapter < ConnectorsSdk::Base::Adapter
14
+ # it's important to have this to generate ID converters between the GitLab ID and the
15
+ # Enterprise Search document ID. The Enterprise Search document ID will be prefixed with the service type,
16
+ # in our case - `gitlab`.
17
+ generate_id_helpers :gitlab, 'gitlab'
18
+
19
+ def self.to_es_document(type, source_doc)
20
+ result = {
21
+ :id => gitlab_id_to_es_id(source_doc[:id]),
22
+ :type => type,
23
+ :url => source_doc[:web_url],
24
+ :body => source_doc[:description],
25
+ :title => source_doc[:name],
26
+ :created_at => source_doc[:created_at],
27
+ :last_modified_at => source_doc[:last_activity_at],
28
+ :visibility => source_doc[:visibility],
29
+ :namespace => if source_doc[:namespace].nil?
30
+ nil
31
+ else
32
+ source_doc[:namespace][:name]
33
+ end
34
+ }
35
+ if source_doc[:_allow_permissions].present?
36
+ result[:_allow_permissions] = source_doc[:_allow_permissions]
37
+ end
38
+ result
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,26 @@
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_sdk/base/config'
10
+
11
+ module ConnectorsSdk
12
+ module GitLab
13
+ class Config < ConnectorsSdk::Base::Config
14
+ attr_reader :index_permissions
15
+
16
+ def initialize(cursors:, index_permissions: false)
17
+ super(:cursors => cursors)
18
+ @index_permissions = index_permissions || false
19
+ end
20
+
21
+ def to_h
22
+ super.merge(:index_permissions => index_permissions)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,71 @@
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_sdk/base/connector'
10
+ require 'connectors_sdk/gitlab/custom_client'
11
+ require 'connectors_sdk/gitlab/adapter'
12
+ require 'connectors_sdk/gitlab/config'
13
+ require 'connectors_sdk/gitlab/extractor'
14
+ require 'rack/utils'
15
+
16
+ module ConnectorsSdk
17
+ module GitLab
18
+ class Connector < ConnectorsSdk::Base::Connector
19
+ SERVICE_TYPE = 'gitlab'
20
+
21
+ def display_name
22
+ 'GitLab Connector'
23
+ end
24
+
25
+ def configurable_fields
26
+ [
27
+ {
28
+ 'key' => 'api_token',
29
+ 'label' => 'API Token'
30
+ },
31
+ {
32
+ 'key' => 'base_url',
33
+ 'label' => 'Base URL'
34
+ }
35
+ ]
36
+ end
37
+
38
+ private
39
+
40
+ def client(params)
41
+ ConnectorsSdk::GitLab::CustomClient.new(
42
+ :base_url => params[:base_url] || ConnectorsSdk::GitLab::API_BASE_URL,
43
+ :api_token => params[:api_token]
44
+ )
45
+ end
46
+
47
+ def config(params)
48
+ ConnectorsSdk::GitLab::Config.new(
49
+ :cursors => params.fetch(:cursors, {}) || {},
50
+ :index_permissions => params.fetch(:index_permissions, false)
51
+ )
52
+ end
53
+
54
+ def extractor_class
55
+ ConnectorsSdk::GitLab::Extractor
56
+ end
57
+
58
+ def custom_client_error
59
+ ConnectorsSdk::GitLab::CustomClient::ClientError
60
+ end
61
+
62
+ def health_check(params)
63
+ # let's do a simple call
64
+ response = client(params).get('user')
65
+ unless response.present? && response.status == 200
66
+ raise "Health check failed with response status #{response.status} and body #{response.body}"
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,40 @@
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_sdk/base/custom_client'
8
+ require 'connectors_shared/middleware/bearer_auth'
9
+ require 'connectors_shared/middleware/basic_auth'
10
+ require 'connectors_shared/middleware/restrict_hostnames'
11
+
12
+ module ConnectorsSdk
13
+ module GitLab
14
+ API_BASE_URL = 'https://gitlab.com/api/v4'
15
+
16
+ class CustomClient < ConnectorsSdk::Base::CustomClient
17
+ class ClientError < ConnectorsShared::ClientError
18
+ attr_reader :status_code, :endpoint
19
+
20
+ def initialize(status_code, endpoint)
21
+ @status_code = status_code
22
+ @endpoint = endpoint
23
+ end
24
+ end
25
+
26
+ def initialize(base_url:, api_token:, ensure_fresh_auth: nil)
27
+ @api_token = api_token
28
+ super(:base_url => base_url, :ensure_fresh_auth => ensure_fresh_auth)
29
+ end
30
+
31
+ def additional_middleware
32
+ [
33
+ ::FaradayMiddleware::FollowRedirects,
34
+ [ConnectorsShared::Middleware::RestrictHostnames, { :allowed_hosts => [base_url, API_BASE_URL] }],
35
+ [ConnectorsShared::Middleware::BearerAuth, { :bearer_auth_token => @api_token }]
36
+ ]
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,123 @@
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_sdk/base/extractor'
10
+ require 'connectors_sdk/gitlab/custom_client'
11
+ require 'connectors_sdk/gitlab/adapter'
12
+ require 'connectors_sdk/gitlab/config'
13
+ require 'rack/utils'
14
+
15
+ module ConnectorsSdk
16
+ module GitLab
17
+ class Extractor < ConnectorsSdk::Base::Extractor
18
+ PAGE_SIZE = 100 # max is 100
19
+
20
+ def yield_document_changes(modified_since: nil)
21
+ query_params = {
22
+ :pagination => :keyset,
23
+ :per_page => PAGE_SIZE,
24
+ :order_by => :id,
25
+ :sort => :desc
26
+ }
27
+ # looks like it's an incremental sync
28
+ if modified_since.present?
29
+ date_since = modified_since.is_a?(Time) ? modified_since : Time.new(modified_since)
30
+ query_params[:last_activity_after] = date_since.iso8601
31
+ end
32
+
33
+ next_page_link = nil
34
+
35
+ loop do
36
+ if next_page_link.present?
37
+ if (matcher = /(https?:[^>]*)/.match(next_page_link))
38
+ clean_query = URI.parse(matcher.captures[0]).query
39
+ query_params = Rack::Utils.parse_query(clean_query)
40
+ else
41
+ raise "Next page link has unexpected format: #{next_page_link}"
42
+ end
43
+ end
44
+ response = client.get('projects', query_params)
45
+
46
+ JSON.parse(response.body).map do |doc|
47
+ doc = doc.with_indifferent_access
48
+ if config.index_permissions
49
+ doc = doc.merge(project_permissions(doc[:id], doc[:visibility]))
50
+ end
51
+ yield :create_or_update, ConnectorsSdk::GitLab::Adapter.to_es_document(:project, doc), nil
52
+ end
53
+
54
+ next_page_link = response.headers['Link'] || nil
55
+ break unless next_page_link.present?
56
+ end
57
+ end
58
+
59
+ def yield_deleted_ids(ids)
60
+ if ids.present?
61
+ ids.each do |id|
62
+ response = client.get("projects/#{id}")
63
+ if response.status == 404
64
+ # not found - assume deleted
65
+ yield id
66
+ else
67
+ unless response.success?
68
+ raise "Could not get a project by ID: #{id}, response code: #{response.status}, response: #{response.body}"
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def yield_permissions(source_user_id)
76
+ result = []
77
+ if source_user_id.present?
78
+ result.push("user:#{source_user_id}")
79
+
80
+ user_response = client.get("users/#{source_user_id}")
81
+ if user_response.success?
82
+ username = JSON.parse(user_response.body).with_indifferent_access[:username]
83
+ query = { :external => true, :username => username }
84
+ external_response = client.get('users', query)
85
+ if external_response.success?
86
+ external_users = Hashie::Array.new(JSON.parse(external_response.body))
87
+ if external_users.empty?
88
+ # the user is not external
89
+ result.push('type:internal')
90
+ end
91
+ else
92
+ raise "Could not check external user status by ID: #{source_user_id}"
93
+ end
94
+ else
95
+ raise "User isn't found by ID: #{source_user_id}"
96
+ end
97
+ end
98
+ yield result
99
+ end
100
+
101
+ private
102
+
103
+ def project_permissions(id, visibility)
104
+ result = []
105
+ if visibility.to_sym == :public || !config.index_permissions
106
+ # visible-to-all
107
+ return {}
108
+ end
109
+ if visibility.to_sym == :internal
110
+ result.push('type:internal')
111
+ end
112
+ response = client.get("projects/#{id}/members/all")
113
+ if response.success?
114
+ members = Hashie::Array.new(JSON.parse(response.body))
115
+ result.concat(members.map { |user| "user:#{user[:id]}" })
116
+ else
117
+ raise "Could not get project members by project ID: #{id}, response code: #{response.status}, response: #{response.body}"
118
+ end
119
+ { :_allow_permissions => result }
120
+ end
121
+ end
122
+ end
123
+ end
@@ -6,22 +6,46 @@
6
6
 
7
7
  # frozen_string_literal: true
8
8
 
9
+ require 'connectors_sdk/base/connector'
9
10
  require 'connectors_sdk/office365/config'
10
11
  require 'connectors_sdk/share_point/extractor'
11
12
  require 'connectors_sdk/share_point/authorization'
12
- require 'connectors_sdk/base/http_call_wrapper'
13
13
 
14
14
  module ConnectorsSdk
15
15
  module SharePoint
16
- class HttpCallWrapper < ConnectorsSdk::Base::HttpCallWrapper
16
+ class Connector < ConnectorsSdk::Base::Connector
17
17
  SERVICE_TYPE = 'share_point'
18
18
 
19
- def name
20
- 'SharePoint'
19
+ def compare_secrets(params)
20
+ missing_secrets?(params)
21
+
22
+ previous_user = client(:access_token => params[:other_secret][:access_token]).me
23
+ equivalent = previous_user.nil? ? false : previous_user.id == client(:access_token => params[:secret][:access_token]).me&.id
24
+
25
+ {
26
+ :equivalent => equivalent
27
+ }
28
+ end
29
+
30
+ def display_name
31
+ 'SharePoint Online'
32
+ end
33
+
34
+ def connection_requires_redirect
35
+ true
21
36
  end
22
37
 
23
- def service_type
24
- SERVICE_TYPE
38
+ def configurable_fields
39
+ [
40
+ {
41
+ 'key' => 'client_id',
42
+ 'label' => 'Client ID'
43
+ },
44
+ {
45
+ 'key' => 'client_secret',
46
+ 'label' => 'Client Secret'
47
+ },
48
+ ]
25
49
  end
26
50
 
27
51
  private
@@ -0,0 +1,62 @@
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_sdk/base/connector'
10
+
11
+ module ConnectorsSdk
12
+ module StubConnector
13
+ class Connector < ConnectorsSdk::Base::Connector
14
+ SERVICE_TYPE = 'stub_connector'
15
+
16
+ def display_name
17
+ 'Stub Connector'
18
+ end
19
+
20
+ def configurable_fields
21
+ [
22
+ {
23
+ 'key' => 'third_party_url',
24
+ 'label' => 'Third Party URL'
25
+ },
26
+ {
27
+ 'key' => 'third_party_api_key',
28
+ 'label' => 'Third Party API Key'
29
+ }
30
+ ]
31
+ end
32
+
33
+ def health_check(_params)
34
+ true
35
+ end
36
+
37
+ def document_batch(_params)
38
+ results = 30.times.map do |i|
39
+ {
40
+ :action => :create_or_update,
41
+ :document => {
42
+ :id => "document_#{i}",
43
+ :type => 'document',
44
+ :body => "contents for document number: #{i}"
45
+ },
46
+ :download => nil
47
+ }
48
+ end
49
+
50
+ [results, {}, true]
51
+ end
52
+
53
+ def deleted(_params)
54
+ []
55
+ end
56
+
57
+ def permissions(_params)
58
+ []
59
+ end
60
+ end
61
+ end
62
+ end
@@ -10,5 +10,17 @@ module ConnectorsShared
10
10
  SUBEXTRACTOR_RESERVED_FIELDS = %w[_subextracted_as_of _subextracted_version].freeze
11
11
  ALLOW_FIELD = '_allow_permissions'.freeze
12
12
  DENY_FIELD = '_deny_permissions'.freeze
13
+
14
+ # The following section reads as following:
15
+ # The job will extract documents until the job queue size will reach
16
+ # JOB_QUEUE_SIZE_IDLE_THRESHOLD items. After that, the job will attempt to sleep
17
+ # for IDLE_SLEEP_TIME seconds and check the queue size again. If the queue is still
18
+ # full, it will sleep for maximum MAX_IDDLE_ATTEMPTS times, and if the queue is still
19
+ # full, then job will be terminated.
20
+ JOB_QUEUE_SIZE_IDLE_THRESHOLD = 500 # How many documents the job queue stores until it sleeps
21
+ IDLE_SLEEP_TIME = 10 # For how long job queue will sleep before checking the queue size again
22
+ MAX_IDLE_ATTEMPTS = 30 # How many consecutive times job will try to sleep until it's destroyed
23
+
24
+ STALE_JOB_TIMEOUT = 60 * 30 # Time in seconds after which the job will be cleaned up if the job is considered stuck
13
25
  end
14
26
  end
@@ -36,7 +36,7 @@ module ConnectorsShared
36
36
  def ips_from_hosts(hosts)
37
37
  hosts&.flat_map do |host|
38
38
  if URL_PATTERN.match(host)
39
- lookup_ips(URI.parse(host).host)
39
+ lookup_ips(Addressable::URI.parse(host).hostname)
40
40
  elsif Resolv::IPv4::Regex.match(host) || Resolv::IPv6::Regex.match(host)
41
41
  IPAddr.new(host)
42
42
  else
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: connectors_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.3.0.0.pre.20220517T144653Z
4
+ version: 8.3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-17 00:00:00.000000000 Z
11
+ date: 2022-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -52,6 +52,118 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: nokogiri
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: tzinfo-data
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: faraday
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: faraday_middleware
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: forwardable
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: hashie
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: httpclient
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: signet
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
55
167
  description: ''
56
168
  email: ent-search-dev@elastic.co
57
169
  executables: []
@@ -66,17 +178,22 @@ files:
66
178
  - lib/connectors_sdk/base/adapter.rb
67
179
  - lib/connectors_sdk/base/authorization.rb
68
180
  - lib/connectors_sdk/base/config.rb
181
+ - lib/connectors_sdk/base/connector.rb
69
182
  - lib/connectors_sdk/base/custom_client.rb
70
183
  - lib/connectors_sdk/base/extractor.rb
71
- - lib/connectors_sdk/base/http_call_wrapper.rb
72
184
  - lib/connectors_sdk/base/registry.rb
73
185
  - lib/connectors_sdk/confluence/adapter.rb
74
186
  - lib/connectors_sdk/confluence/custom_client.rb
75
187
  - lib/connectors_sdk/confluence/extractor.rb
76
188
  - lib/connectors_sdk/confluence_cloud/authorization.rb
189
+ - lib/connectors_sdk/confluence_cloud/connector.rb
77
190
  - lib/connectors_sdk/confluence_cloud/custom_client.rb
78
191
  - lib/connectors_sdk/confluence_cloud/extractor.rb
79
- - lib/connectors_sdk/confluence_cloud/http_call_wrapper.rb
192
+ - lib/connectors_sdk/gitlab/adapter.rb
193
+ - lib/connectors_sdk/gitlab/config.rb
194
+ - lib/connectors_sdk/gitlab/connector.rb
195
+ - lib/connectors_sdk/gitlab/custom_client.rb
196
+ - lib/connectors_sdk/gitlab/extractor.rb
80
197
  - lib/connectors_sdk/helpers/atlassian_time_formatter.rb
81
198
  - lib/connectors_sdk/office365/adapter.rb
82
199
  - lib/connectors_sdk/office365/config.rb
@@ -84,8 +201,9 @@ files:
84
201
  - lib/connectors_sdk/office365/extractor.rb
85
202
  - lib/connectors_sdk/share_point/adapter.rb
86
203
  - lib/connectors_sdk/share_point/authorization.rb
204
+ - lib/connectors_sdk/share_point/connector.rb
87
205
  - lib/connectors_sdk/share_point/extractor.rb
88
- - lib/connectors_sdk/share_point/http_call_wrapper.rb
206
+ - lib/connectors_sdk/stub_connector/connector.rb
89
207
  - lib/connectors_shared.rb
90
208
  - lib/connectors_shared/constants.rb
91
209
  - lib/connectors_shared/errors.rb
@@ -98,14 +216,13 @@ files:
98
216
  - lib/connectors_shared/middleware/bearer_auth.rb
99
217
  - lib/connectors_shared/middleware/restrict_hostnames.rb
100
218
  - lib/connectors_shared/monitor.rb
101
- - lib/stubs/enterprise_search/exception_tracking.rb
102
219
  homepage: https://github.com/elastic/connectors
103
220
  licenses:
104
221
  - Elastic-2.0
105
222
  metadata:
106
- revision: 9f25f35e17ffb36dfda754d657794ed9b5d2d75a
107
- repository: git@github.com:elastic/connectors.git
108
- post_install_message:
223
+ revision: e45e2d01a4902ee7888a1223c18d78e60a5bfcde
224
+ repository: git@github.com:elastic/ent-search-connectors.git
225
+ post_install_message:
109
226
  rdoc_options: []
110
227
  require_paths:
111
228
  - lib
@@ -116,12 +233,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
116
233
  version: '0'
117
234
  required_rubygems_version: !ruby/object:Gem::Requirement
118
235
  requirements:
119
- - - ">"
236
+ - - ">="
120
237
  - !ruby/object:Gem::Version
121
- version: 1.3.1
238
+ version: '0'
122
239
  requirements: []
123
240
  rubygems_version: 3.0.3.1
124
- signing_key:
241
+ signing_key:
125
242
  specification_version: 4
126
243
  summary: Gem containing apis used by Enterprise Search and implementations of Connectors
127
244
  test_files: []
@@ -1,59 +0,0 @@
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_sdk/atlassian/config'
10
- require 'connectors_sdk/confluence_cloud/extractor'
11
- require 'connectors_sdk/confluence_cloud/authorization'
12
- require 'connectors_sdk/confluence_cloud/custom_client'
13
- require 'connectors_sdk/base/http_call_wrapper'
14
-
15
- module ConnectorsSdk
16
- module ConfluenceCloud
17
- class HttpCallWrapper < ConnectorsSdk::Base::HttpCallWrapper
18
- SERVICE_TYPE = 'confluence_cloud'
19
-
20
- def name
21
- 'Confluence Cloud'
22
- end
23
-
24
- def service_type
25
- SERVICE_TYPE
26
- end
27
-
28
- private
29
-
30
- def extractor_class
31
- ConnectorsSdk::ConfluenceCloud::Extractor
32
- end
33
-
34
- def authorization
35
- ConnectorsSdk::ConfluenceCloud::Authorization
36
- end
37
-
38
- def client(params)
39
- ConnectorsSdk::ConfluenceCloud::CustomClient.new(:base_url => base_url(params[:cloud_id]), :access_token => params[:access_token])
40
- end
41
-
42
- def custom_client_error
43
- ConnectorsSdk::Atlassian::CustomClient::ClientError
44
- end
45
-
46
- def config(params)
47
- ConnectorsSdk::Atlassian::Config.new(:base_url => base_url(params[:cloud_id]), :cursors => params.fetch(:cursors, {}) || {})
48
- end
49
-
50
- def health_check(params)
51
- client(params).me
52
- end
53
-
54
- def base_url(cloud_id)
55
- "https://api.atlassian.com/ex/confluence/#{cloud_id}"
56
- end
57
- end
58
- end
59
- end
@@ -1,43 +0,0 @@
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 EnterpriseSearch
8
- class ExceptionTracking
9
- def self.capture_message(message, context = {})
10
- AppConfig.connectors_logger.error { "Error: #{message}. Context: #{context.inspect}" }
11
-
12
- # When the method is called from a rescue block, our return value may leak outside of its
13
- # intended scope, so let's explicitly return nil here to be safe.
14
- nil
15
- end
16
-
17
- def self.log_exception(exception, message = nil, context: nil, logger: AppConfig.connectors_logger)
18
- logger.error { message } if message
19
- logger.error { generate_stack_trace(exception) }
20
- logger.error { "Context: #{context.inspect}" } if context
21
- end
22
-
23
- def self.generate_error_message(exception, message, context)
24
- context = { :message_id => exception.id }.merge(context || {}) if exception.respond_to?(:id)
25
- context_message = context && "Context: #{context.inspect}"
26
- ['Exception', message, exception.class.to_s, exception.message, context_message]
27
- .compact
28
- .map { |part| part.to_s.dup.force_encoding('UTF-8') }
29
- .join(': ')
30
- end
31
-
32
- def self.generate_stack_trace(exception)
33
- full_message = exception.full_message
34
-
35
- cause = exception
36
- while cause.cause != cause && (cause = cause.cause)
37
- full_message << "Cause:\n#{cause.full_message}"
38
- end
39
-
40
- full_message.dup.force_encoding('UTF-8')
41
- end
42
- end
43
- end