connectors_sdk 8.3.0.0.pre.20220510T144908Z → 8.3.2.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: 94b75b9fa3a5f0c46a271a34d073f71977629db6095b6364c8710b0aab92374b
4
- data.tar.gz: d133f34052f43e0b8b65ba10eb946c55a6ef421a6296d2d4438e7f4f92b45696
3
+ metadata.gz: 929c0ce8ad8eaf2ab34849ee4f0b476c37e5cfe12994bf73aa183be58c350213
4
+ data.tar.gz: 9e7189b26959b4bb74e097a6e0a2effb3ade9a0b730dbccee5c23f5d0cedebd1
5
5
  SHA512:
6
- metadata.gz: 712b819efcfa755ce19e4cdb7060dbadefecf37321d07bfe4e7bc9b18254e0f11f5fc29967ff171b1314064d77427bde730ab400a4a2dcde89b31dc6344d4a34
7
- data.tar.gz: fa1fedb0c7449b9b2b50b1a56a710178cd3343a108500938244987eb31c694ab0d0214f383df69b6efa6b384eb558697553813c9b93cb142f011b374e05f2c49
6
+ metadata.gz: 6ea84dda6c9c5980b2fa089e2ac049a8ce1d27ff98761f20bb5e760dbbf6851c452cdcb35e40d274cff0c692da37e9a77be029fd286bc38672ea3597ab013db7
7
+ data.tar.gz: 837884527017d23bb33a1370f6500db9b8d37afee351ffd6243d852309120f69d2dd1bcf18a70556785d2b460e011276b8d7b621d09c631519c7aa95603b32e9
@@ -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,43 +10,55 @@ 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
  )
23
35
  end
24
36
 
25
- def document_batch(params)
37
+ def extract(params)
26
38
  convert_third_party_errors do
27
- results = []
28
-
29
39
  extractor = extractor(params)
30
40
 
31
- extractor.yield_document_changes(:break_after_page => true, :modified_since => extractor.config.cursors['modified_since']) do |action, doc, download_args_and_proc|
41
+ extractor.yield_document_changes(:modified_since => extractor.config.cursors[:modified_since]) do |action, doc, download_args_and_proc|
32
42
  download_obj = nil
33
43
  if download_args_and_proc
34
44
  download_obj = {
35
- id: download_args_and_proc[0],
36
- name: download_args_and_proc[1],
37
- size: download_args_and_proc[2],
38
- download_args: download_args_and_proc[3]
45
+ id: download_args_and_proc[0],
46
+ name: download_args_and_proc[1],
47
+ size: download_args_and_proc[2],
48
+ download_args: download_args_and_proc[3]
39
49
  }
40
50
  end
41
51
 
42
- results << {
43
- :action => action,
44
- :document => doc,
45
- :download => download_obj
52
+ doc = {
53
+ :action => action,
54
+ :document => doc,
55
+ :download => download_obj
46
56
  }
57
+
58
+ yield doc
47
59
  end
48
60
 
49
- [results, extractor.config.cursors, extractor.completed]
61
+ extractor.config.to_h[:cursors]
50
62
  end
51
63
  end
52
64
 
@@ -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
@@ -74,7 +74,7 @@ module ConnectorsSdk
74
74
  faraday.use(*middleware_config)
75
75
  end
76
76
 
77
- faraday.adapter(:httpclient)
77
+ faraday.adapter :httpclient
78
78
  end
79
79
  end
80
80
 
@@ -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
@@ -20,7 +20,7 @@ module ConnectorsSdk
20
20
 
21
21
  ConnectorsSdk::Base::Extractor::TRANSIENT_SERVER_ERROR_CLASSES << Atlassian::CustomClient::ServiceUnavailableError
22
22
 
23
- def yield_document_changes(modified_since: nil, break_after_page: false)
23
+ def yield_document_changes(modified_since: nil)
24
24
  @space_permissions_cache = {}
25
25
  @content_restriction_cache = {}
26
26
  yield_spaces do |space|
@@ -50,11 +50,6 @@ module ConnectorsSdk
50
50
  yield :create_or_update, Confluence::Adapter.es_document_from_confluence_content(content, content_base_url, restrictions)
51
51
  end
52
52
  end
53
-
54
- if break_after_page
55
- @completed = true
56
- break
57
- end
58
53
  end
59
54
  end
60
55
 
@@ -87,7 +82,7 @@ module ConnectorsSdk
87
82
  private
88
83
 
89
84
  def content_base_url
90
- 'https://workplace-search.atlassian.net/wiki'
85
+ config.base_url
91
86
  end
92
87
 
93
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