connectors_sdk 8.3.0.0.pre.20220414T060419Z → 8.3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/connectors_sdk/atlassian/config.rb +27 -0
  3. data/lib/connectors_sdk/atlassian/custom_client.rb +87 -0
  4. data/lib/connectors_sdk/base/adapter.rb +7 -8
  5. data/lib/connectors_sdk/base/authorization.rb +89 -0
  6. data/lib/connectors_sdk/base/connector.rb +166 -0
  7. data/lib/connectors_sdk/base/custom_client.rb +1 -2
  8. data/lib/connectors_sdk/base/extractor.rb +6 -2
  9. data/lib/connectors_sdk/base/registry.rb +9 -4
  10. data/lib/connectors_sdk/confluence/adapter.rb +216 -0
  11. data/lib/connectors_sdk/confluence/custom_client.rb +143 -0
  12. data/lib/connectors_sdk/confluence/extractor.rb +265 -0
  13. data/lib/connectors_sdk/confluence_cloud/authorization.rb +64 -0
  14. data/lib/connectors_sdk/confluence_cloud/connector.rb +84 -0
  15. data/lib/connectors_sdk/confluence_cloud/custom_client.rb +61 -0
  16. data/lib/connectors_sdk/confluence_cloud/extractor.rb +59 -0
  17. data/lib/connectors_sdk/helpers/atlassian_time_formatter.rb +10 -0
  18. data/lib/connectors_sdk/office365/adapter.rb +7 -7
  19. data/lib/connectors_sdk/office365/config.rb +1 -0
  20. data/lib/connectors_sdk/office365/custom_client.rb +20 -39
  21. data/lib/connectors_sdk/office365/extractor.rb +18 -34
  22. data/lib/connectors_sdk/share_point/adapter.rb +24 -12
  23. data/lib/connectors_sdk/share_point/authorization.rb +14 -62
  24. data/lib/connectors_sdk/share_point/connector.rb +82 -0
  25. data/lib/connectors_sdk/share_point/extractor.rb +2 -2
  26. data/lib/connectors_sdk/stub_connector/connector.rb +62 -0
  27. data/lib/connectors_shared/constants.rb +12 -0
  28. data/lib/connectors_shared/exception_tracking.rb +4 -4
  29. data/lib/connectors_shared/extraction_utils.rb +109 -0
  30. data/lib/connectors_shared/job_status.rb +18 -0
  31. data/lib/connectors_shared/middleware/basic_auth.rb +27 -0
  32. data/lib/connectors_shared/middleware/bearer_auth.rb +27 -0
  33. data/lib/connectors_shared/middleware/restrict_hostnames.rb +73 -0
  34. data/lib/connectors_shared/monitor.rb +3 -3
  35. data/lib/connectors_shared.rb +1 -0
  36. data/lib/stubs/enterprise_search/exception_tracking.rb +43 -0
  37. metadata +30 -16
  38. data/lib/connectors_sdk/base/.config.rb.un~ +0 -0
  39. data/lib/connectors_sdk/base/.connectors.rb.un~ +0 -0
  40. data/lib/connectors_sdk/base/.registry.rb.un~ +0 -0
  41. data/lib/connectors_sdk/share_point/.http_call_wrapper.rb.un~ +0 -0
  42. data/lib/connectors_sdk/share_point/http_call_wrapper.rb +0 -117
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a44a80ba5fe5032b6254fb8a2db25516542df5818044d627b6ba31f652a7812f
4
- data.tar.gz: 52d1245cc24ab6e1a42c00483ec4a8ece48ede4a6465c9b46b3bf5e18c979678
3
+ metadata.gz: 21bd11a44ed7c45fa04b218346fda20d946b97ec8f7025c2cfb8c8f92321d6a3
4
+ data.tar.gz: b388f972974f32c59d719b809975229125e293045dd35559546fefe5485e1fc3
5
5
  SHA512:
6
- metadata.gz: 7a4021e1d7b83ec7334913f1ef5e684e02296097f223e96610cb31eb5502c36f9b45b03eeaf80e876aac5bcc531f85d10445917459b86cedc30d6609b33f5351
7
- data.tar.gz: ca04ab72f48ed6e76b3a0e0cc8c260f81779ea906325a782eb61456c9c5be231043d7a73ca9625df63459460de587c65aa352fb2e1150fc8c92a253536fcd634
6
+ metadata.gz: 88aa76506d6f2af049ea2a2ff54797270cef692319dd9ca647d33903a9fb3bb8bb274d9d99a3f94fe4d54cfce1722b31d63203cd157b627ccf3b2ca541b63523
7
+ data.tar.gz: 3faf8e16b18c0abd4f13fa988de8a6bb03e95bce5738389f132748fc17a230a9e516eb8b9f898d60aeb8a869f1ad1dadbb8606178e01be7d9ccc63ca98dcf681
@@ -0,0 +1,27 @@
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 Atlassian
13
+ class Config < ConnectorsSdk::Base::Config
14
+ attr_reader :base_url, :index_permissions
15
+
16
+ def initialize(cursors:, base_url:, index_permissions: false)
17
+ super(:cursors => cursors)
18
+ @base_url = base_url
19
+ @index_permissions = index_permissions
20
+ end
21
+
22
+ def to_h
23
+ super.merge(:base_url => base_url)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,87 @@
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 'faraday_middleware'
10
+
11
+ require 'connectors_shared/middleware/bearer_auth'
12
+ require 'connectors_shared/middleware/restrict_hostnames'
13
+ require 'connectors_sdk/base/custom_client'
14
+
15
+ module ConnectorsSdk
16
+ module Atlassian
17
+ class CustomClient < ConnectorsSdk::Base::CustomClient
18
+ class ClientError < ConnectorsShared::ClientError
19
+ attr_reader :url, :status_code, :message
20
+
21
+ def initialize(url, status_code, message)
22
+ super("Failed to call #{url} because #{status_code}: #{message}")
23
+ @url = url
24
+ @status_code = status_code
25
+ @message = message
26
+ end
27
+ end
28
+ class ServiceUnavailableError < ClientError; end
29
+ class ContentConvertibleError < ClientError; end
30
+
31
+ MEDIA_API_BASE_URL = 'https://api.media.atlassian.com'
32
+
33
+ attr_reader :base_url, :access_token
34
+
35
+ def initialize(base_url:, access_token:, ensure_fresh_auth: nil)
36
+ @access_token = access_token
37
+ super(:base_url => base_url, :ensure_fresh_auth => ensure_fresh_auth)
38
+ end
39
+
40
+ def default_middleware
41
+ [] # Ignoring Base default for now, but we should probably revert to using Base default?
42
+ end
43
+
44
+ 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 }]
49
+ ]
50
+ end
51
+
52
+ def update_auth_data!(new_access_token)
53
+ @access_token = new_access_token
54
+ middleware!
55
+ http_client! # force a new client to pick up new middleware
56
+
57
+ self
58
+ end
59
+
60
+ def download(url)
61
+ response = get(url)
62
+ unless HTTP::Status.successful?(response.status)
63
+ raise ClientError.new(url, response.status, response.body)
64
+ end
65
+ response
66
+ end
67
+
68
+ private
69
+
70
+ def parse_and_raise_if_necessary!(response)
71
+ unless response.success?
72
+ status_code = response.status.to_i
73
+ atlassian_error_klass =
74
+ if status_code == 504
75
+ ServiceUnavailableError
76
+ elsif status_code == 400 && response.body.include?('is not ContentConvertible or API available')
77
+ ContentConvertibleError
78
+ else
79
+ ClientError
80
+ end
81
+ raise atlassian_error_klass.new(response.env.url.to_s, status_code, response.body)
82
+ end
83
+ JSON.parse(response.body)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -10,7 +10,6 @@ require 'active_support/core_ext/object/deep_dup'
10
10
  require 'connectors_shared'
11
11
  require 'connectors_shared/extension_mapping_util'
12
12
  require 'date'
13
- require 'active_support/all'
14
13
  require 'mime-types'
15
14
 
16
15
  module ConnectorsSdk
@@ -25,19 +24,19 @@ module ConnectorsSdk
25
24
  end
26
25
 
27
26
  def self.generate_id_helpers(method_prefix, id_prefix)
28
- define_singleton_method("#{method_prefix}_id_to_fp_id") do |id|
27
+ define_singleton_method("#{method_prefix}_id_to_es_id") do |id|
29
28
  "#{id_prefix}_#{id}"
30
29
  end
31
30
 
32
- define_singleton_method("fp_id_is_#{method_prefix}_id?") do |fp_id|
33
- regex_match = /#{id_prefix}_(.+)$/.match(fp_id)
31
+ define_singleton_method("es_id_is_#{method_prefix}_id?") do |es_id|
32
+ regex_match = /#{id_prefix}_(.+)$/.match(es_id)
34
33
  regex_match.present? && regex_match.size == 2
35
34
  end
36
35
 
37
- define_singleton_method("fp_id_to_#{method_prefix}_id") do |fp_id|
38
- regex_match = /#{id_prefix}_(.+)$/.match(fp_id)
36
+ define_singleton_method("es_id_to_#{method_prefix}_id") do |es_id|
37
+ regex_match = /#{id_prefix}_(.+)$/.match(es_id)
39
38
 
40
- raise ArgumentError, "Invalid id #{fp_id} for source with method prefix #{method_prefix}." if regex_match.nil? || regex_match.length != 2
39
+ raise ArgumentError, "Invalid id #{es_id} for source with method prefix #{method_prefix}." if regex_match.nil? || regex_match.length != 2
41
40
  regex_match[1]
42
41
  end
43
42
  end
@@ -92,7 +91,7 @@ module ConnectorsSdk
92
91
  nil
93
92
  end
94
93
 
95
- def self.swiftype_document_from_configured_object_base(object_type:, object:, fields:)
94
+ def self.es_document_from_configured_object_base(object_type:, object:, fields:)
96
95
  object_as_json = object.as_json
97
96
 
98
97
  adapted_object = {
@@ -0,0 +1,89 @@
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_shared'
10
+ require 'signet'
11
+ require 'signet/oauth_2'
12
+ require 'signet/oauth_2/client'
13
+
14
+ module ConnectorsSdk
15
+ module Base
16
+ class Authorization
17
+ class << self
18
+ def authorization_uri(params)
19
+ missing = missing_fields(params, %w[client_id])
20
+ unless missing.blank?
21
+ raise ConnectorsShared::ClientError.new("Missing required fields: #{missing.join(', ')}")
22
+ end
23
+
24
+ params[:response_type] = 'code'
25
+ params[:additional_parameters] = additional_parameters
26
+ client = oauth_client(params)
27
+ client.authorization_uri.to_s
28
+ end
29
+
30
+ def access_token(params)
31
+ missing = missing_fields(params, %w[client_id client_secret code redirect_uri])
32
+ unless missing.blank?
33
+ raise ConnectorsShared::ClientError.new("Missing required fields: #{missing.join(', ')}")
34
+ end
35
+
36
+ params[:grant_type] = 'authorization_code'
37
+ client = oauth_client(params)
38
+ client.fetch_access_token
39
+ end
40
+
41
+ def refresh(params)
42
+ missing = missing_fields(params, %w[client_id client_secret refresh_token])
43
+ unless missing.blank?
44
+ raise ConnectorsShared::ClientError.new("Missing required fields: #{missing.join(', ')}")
45
+ end
46
+
47
+ params[:grant_type] = 'refresh_token'
48
+ client = oauth_client(params)
49
+ client.refresh!
50
+ rescue StandardError => e
51
+ ConnectorsShared::ExceptionTracking.log_exception(e)
52
+ raise ConnectorsShared::TokenRefreshFailedError
53
+ end
54
+
55
+ def oauth_client(params)
56
+ options = params.merge(
57
+ :authorization_uri => authorization_url,
58
+ :token_credential_uri => token_credential_uri,
59
+ :scope => oauth_scope
60
+ )
61
+ options[:state] = JSON.dump(options[:state]) if options[:state]
62
+ Signet::OAuth2::Client.new(options)
63
+ end
64
+
65
+ def missing_fields(params, required = [])
66
+ Array.wrap(required).select { |field| params[field.to_sym].nil? }
67
+ end
68
+
69
+ def oauth_scope
70
+ raise 'Not implemented for this connector'
71
+ end
72
+
73
+ private
74
+
75
+ def authorization_url
76
+ raise 'Not implemented for this connector'
77
+ end
78
+
79
+ def token_credential_uri
80
+ raise 'Not implemented for this connector'
81
+ end
82
+
83
+ def additional_parameters
84
+ raise 'Not implemented for this connector'
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,166 @@
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 'bson'
10
+
11
+ module ConnectorsSdk
12
+ module Base
13
+ class Connector
14
+ def extractor(params)
15
+ content_source_id = params.fetch(:content_source_id)
16
+ secret_storage = params[:secret_storage]
17
+
18
+ extractor_class.new(
19
+ content_source_id: content_source_id || "GENERATED-#{BSON::ObjectId.new}",
20
+ service_type: service_type,
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
+ },
32
+ config: config(params),
33
+ features: params.fetch(:features, {}) || {}
34
+ )
35
+ end
36
+
37
+ def extract(params)
38
+ convert_third_party_errors do
39
+ extractor = extractor(params)
40
+
41
+ extractor.yield_document_changes(:modified_since => extractor.config.cursors[:modified_since]) do |action, doc, download_args_and_proc|
42
+ download_obj = nil
43
+ if download_args_and_proc
44
+ download_obj = {
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]
49
+ }
50
+ end
51
+
52
+ doc = {
53
+ :action => action,
54
+ :document => doc,
55
+ :download => download_obj
56
+ }
57
+
58
+ yield doc
59
+ end
60
+
61
+ extractor.config.to_h[:cursors]
62
+ end
63
+ end
64
+
65
+ def deleted(params)
66
+ convert_third_party_errors do
67
+ results = []
68
+ extractor(params).yield_deleted_ids(params[:ids]) do |id|
69
+ results << id
70
+ end
71
+ results
72
+ end
73
+ end
74
+
75
+ def permissions(params)
76
+ convert_third_party_errors do
77
+ extractor(params).yield_permissions(params[:user_id]) do |permissions|
78
+ return permissions
79
+ end
80
+ end
81
+ end
82
+
83
+ def download(params)
84
+ extractor(params).download(params[:meta])
85
+ end
86
+
87
+ def authorization_uri(params)
88
+ authorization.authorization_uri(params)
89
+ end
90
+
91
+ def access_token(params)
92
+ authorization.access_token(params)
93
+ end
94
+
95
+ def refresh(params)
96
+ authorization.refresh(params)
97
+ end
98
+
99
+ def source_status(params)
100
+ health_check(params)
101
+ { :status => 'OK', :statusCode => 200, :message => "Connected to #{display_name}" }
102
+ rescue StandardError => e
103
+ { :status => 'FAILURE', :statusCode => e.is_a?(custom_client_error) ? e.status_code : 500, :message => e.message }
104
+ end
105
+
106
+ def compare_secrets(*)
107
+ raise 'Not implemented for this connector'
108
+ end
109
+
110
+ def display_name
111
+ raise 'Not implemented for this connector'
112
+ end
113
+
114
+ def service_type
115
+ self.class::SERVICE_TYPE
116
+ end
117
+
118
+ def connection_requires_redirect
119
+ false
120
+ end
121
+
122
+ def configurable_fields
123
+ []
124
+ end
125
+
126
+ private
127
+
128
+ def convert_third_party_errors
129
+ yield
130
+ rescue custom_client_error => e
131
+ raise e.status_code == 401 ? ConnectorsShared::InvalidTokenError : e
132
+ end
133
+
134
+ def extractor_class
135
+ raise 'Not implemented for this connector'
136
+ end
137
+
138
+ def authorization
139
+ raise 'Not implemented for this connector'
140
+ end
141
+
142
+ def client(*)
143
+ raise 'Not implemented for this connector'
144
+ end
145
+
146
+ def custom_client_error
147
+ raise 'Not implemented for this connector'
148
+ end
149
+
150
+ def config(*)
151
+ raise 'Not implemented for this connector'
152
+ end
153
+
154
+ def health_check(*)
155
+ raise 'Not implemented for this connector'
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
164
+ end
165
+ end
166
+ end
@@ -11,7 +11,6 @@ require 'active_support/core_ext/numeric/time'
11
11
  require 'active_support/core_ext/object/deep_dup'
12
12
  require 'connectors_shared'
13
13
  require 'date'
14
- require 'active_support/all'
15
14
 
16
15
  module ConnectorsSdk
17
16
  module Base
@@ -75,7 +74,7 @@ module ConnectorsSdk
75
74
  faraday.use(*middleware_config)
76
75
  end
77
76
 
78
- faraday.adapter(:httpclient)
77
+ faraday.adapter :httpclient
79
78
  end
80
79
  end
81
80
 
@@ -9,10 +9,11 @@ require 'httpclient'
9
9
  require 'active_support/core_ext/array/wrap'
10
10
  require 'active_support/core_ext/numeric/time'
11
11
  require 'active_support/core_ext/object/deep_dup'
12
- require 'connectors_shared'
13
12
  require 'date'
14
- require 'active_support/all'
13
+
14
+ require 'connectors_shared'
15
15
  require 'stubs/connectors/stats' unless defined?(Rails)
16
+ require 'stubs/connectors' unless defined?(Rails)
16
17
 
17
18
  module ConnectorsSdk
18
19
  module Base
@@ -187,16 +188,19 @@ module ConnectorsSdk
187
188
  end
188
189
 
189
190
  def permissions(source_user_id, &block)
191
+ result = []
190
192
  Connectors::Stats.measure("extractor.#{Connectors::Stats.class_key(self.class)}.permissions") do
191
193
  with_auth_tokens_and_retry do
192
194
  Connectors::Stats.measure("extractor.#{Connectors::Stats.class_key(self.class)}.yield_permissions") do
193
195
  yield_permissions(source_user_id) do |permissions|
194
196
  log_info("Extracted #{permissions.size} permissions for source user #{source_user_id}")
197
+ result = permissions
195
198
  block.call(permissions) if block_given?
196
199
  end
197
200
  end
198
201
  end
199
202
  end
203
+ result.each
200
204
  end
201
205
 
202
206
  ConnectorsShared::Logger::SUPPORTED_LOG_LEVELS.each do |log_level|
@@ -17,16 +17,21 @@ module ConnectorsSdk
17
17
  @connectors[name] = klass
18
18
  end
19
19
 
20
- def connector(name)
21
- @connectors[name].new
20
+ def connector_class(name)
21
+ @connectors[name]
22
22
  end
23
23
  end
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'
31
+ require_relative '../share_point/connector'
32
+ require_relative '../confluence_cloud/connector'
29
33
 
30
- REGISTRY.register(ConnectorsSdk::SharePoint::SERVICE_TYPE, ConnectorsSdk::SharePoint::HttpCallWrapper)
34
+ REGISTRY.register(ConnectorsSdk::SharePoint::Connector::SERVICE_TYPE, ConnectorsSdk::SharePoint::Connector)
35
+ REGISTRY.register(ConnectorsSdk::ConfluenceCloud::Connector::SERVICE_TYPE, ConnectorsSdk::ConfluenceCloud::Connector)
31
36
  end
32
37
  end