connectors_sdk 8.3.0.0.pre.20220414T060419Z
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.
- checksums.yaml +7 -0
- data/LICENSE +93 -0
- data/NOTICE.txt +2 -0
- data/lib/connectors_sdk/base/.config.rb.un~ +0 -0
- data/lib/connectors_sdk/base/.connectors.rb.un~ +0 -0
- data/lib/connectors_sdk/base/.registry.rb.un~ +0 -0
- data/lib/connectors_sdk/base/adapter.rb +118 -0
- data/lib/connectors_sdk/base/config.rb +27 -0
- data/lib/connectors_sdk/base/custom_client.rb +112 -0
- data/lib/connectors_sdk/base/extractor.rb +256 -0
- data/lib/connectors_sdk/base/registry.rb +32 -0
- data/lib/connectors_sdk/office365/adapter.rb +153 -0
- data/lib/connectors_sdk/office365/config.rb +37 -0
- data/lib/connectors_sdk/office365/custom_client.rb +319 -0
- data/lib/connectors_sdk/office365/extractor.rb +230 -0
- data/lib/connectors_sdk/share_point/.http_call_wrapper.rb.un~ +0 -0
- data/lib/connectors_sdk/share_point/adapter.rb +47 -0
- data/lib/connectors_sdk/share_point/authorization.rb +91 -0
- data/lib/connectors_sdk/share_point/extractor.rb +31 -0
- data/lib/connectors_sdk/share_point/http_call_wrapper.rb +117 -0
- data/lib/connectors_sdk.rb +16 -0
- data/lib/connectors_shared/constants.rb +14 -0
- data/lib/connectors_shared/errors.rb +126 -0
- data/lib/connectors_shared/exception_tracking.rb +39 -0
- data/lib/connectors_shared/extension_mapping_util.rb +123 -0
- data/lib/connectors_shared/logger.rb +33 -0
- data/lib/connectors_shared/monitor.rb +99 -0
- data/lib/connectors_shared.rb +12 -0
- metadata +114 -0
@@ -0,0 +1,230 @@
|
|
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/office365/custom_client'
|
10
|
+
require 'connectors_sdk/base/extractor'
|
11
|
+
|
12
|
+
module ConnectorsSdk
|
13
|
+
module Office365
|
14
|
+
class Extractor < ConnectorsSdk::Base::Extractor
|
15
|
+
DRIVE_IDS_CURSOR_KEY = 'drive_ids'.freeze
|
16
|
+
|
17
|
+
def yield_document_changes(modified_since: nil, break_after_page: false, &block)
|
18
|
+
drives_to_index.each do |drive|
|
19
|
+
drive_id = drive.id
|
20
|
+
|
21
|
+
if break_after_page
|
22
|
+
current_drive_id = config.cursors['current_drive_id']
|
23
|
+
if current_drive_id.present? && current_drive_id > drive_id # they come alpha sorted
|
24
|
+
next
|
25
|
+
end
|
26
|
+
config.cursors['current_drive_id'] = drive_id
|
27
|
+
end
|
28
|
+
|
29
|
+
drive_owner_name = drive.dig(:owner, :user, :displayName)
|
30
|
+
drive_name = drive.name
|
31
|
+
|
32
|
+
drive_id_to_delta_link = config.cursors.fetch(DRIVE_IDS_CURSOR_KEY, {})
|
33
|
+
begin
|
34
|
+
if start_delta_link = drive_id_to_delta_link[drive_id]
|
35
|
+
log_debug("Starting an incremental crawl with cursor for #{service_type.classify} with drive_id: #{drive_id}")
|
36
|
+
begin
|
37
|
+
yield_changes(drive_id, :start_delta_link => start_delta_link, :drive_owner_name => drive_owner_name, :drive_name => drive_name, :break_after_page => break_after_page, &block)
|
38
|
+
rescue ConnectorsSdk::Office365::CustomClient::Office365InvalidCursorsError
|
39
|
+
log_warn("Error listing changes with start_delta_link: #{start_delta_link}, falling back to full crawl")
|
40
|
+
yield_drive_items(drive_id, :drive_owner_name => drive_owner_name, :drive_name => drive_name, :break_after_page => break_after_page, &block)
|
41
|
+
end
|
42
|
+
elsif modified_since.present?
|
43
|
+
log_debug("Starting an incremental crawl using last_modified (no cursor found) for #{service_type.classify} with drive_id: #{drive_id}")
|
44
|
+
yield_changes(drive_id, :last_modified => modified_since, :drive_owner_name => drive_owner_name, :drive_name => drive_name, :break_after_page => break_after_page, &block)
|
45
|
+
else
|
46
|
+
log_debug("Starting a full crawl #{service_type.classify} with drive_id: #{drive_id}")
|
47
|
+
yield_drive_items(drive_id, :drive_owner_name => drive_owner_name, :drive_name => drive_name, :break_after_page => break_after_page, &block)
|
48
|
+
end
|
49
|
+
rescue ConnectorsSdk::Office365::CustomClient::ClientError => e
|
50
|
+
log_warn("Error searching and listing drive #{drive_id}")
|
51
|
+
capture_exception(e)
|
52
|
+
end
|
53
|
+
|
54
|
+
if break_after_page && config.cursors['page_cursor'].present?
|
55
|
+
break
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
if break_after_page && config.cursors['page_cursor'].blank?
|
60
|
+
@completed = true
|
61
|
+
config.overwrite_cursors!(retrieve_latest_cursors)
|
62
|
+
log_debug("Completed #{modified_since.nil? ? 'full' : 'incremental'} extraction")
|
63
|
+
end
|
64
|
+
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def yield_deleted_ids(ids)
|
69
|
+
ids.each do |id|
|
70
|
+
yield id unless existing_drive_item_ids.include?(id)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def retrieve_latest_cursors
|
75
|
+
delta_links_for_drive_ids = drives_to_index.map(&:id).each_with_object({}) do |drive_id, h|
|
76
|
+
h[drive_id] = client.get_latest_delta_link(drive_id)
|
77
|
+
rescue ConnectorsSdk::Office365::CustomClient::ClientError => e
|
78
|
+
log_warn("Error getting delta link for #{drive_id}")
|
79
|
+
capture_exception(e)
|
80
|
+
raise e
|
81
|
+
end
|
82
|
+
|
83
|
+
{
|
84
|
+
DRIVE_IDS_CURSOR_KEY => delta_links_for_drive_ids
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def yield_permissions(source_user_id)
|
89
|
+
permissions = [source_user_id]
|
90
|
+
client.user_groups(source_user_id, %w(id displayName)).each do |next_group|
|
91
|
+
# Adding "Members" suffix since that is how the item permissions endpoint return group permissions
|
92
|
+
permissions << "#{next_group.displayName} Members"
|
93
|
+
end
|
94
|
+
|
95
|
+
yield permissions.uniq
|
96
|
+
rescue ConnectorsSdk::Office365::CustomClient::ClientError => e
|
97
|
+
# if a user is deleted, client.user_groups will throw 404 Not Found error, saving another call to get user profile
|
98
|
+
if e.status_code == 404
|
99
|
+
log_warn("Could not find a user with id #{source_user_id}")
|
100
|
+
yield []
|
101
|
+
else
|
102
|
+
raise
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def download(item)
|
107
|
+
download_url = item[:download_url]
|
108
|
+
client.download_item(download_url)
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def drives
|
114
|
+
raise NotImplementedError
|
115
|
+
end
|
116
|
+
|
117
|
+
def drives_to_index
|
118
|
+
@drives_to_index ||= begin
|
119
|
+
value = if config.index_all_drives?
|
120
|
+
drives
|
121
|
+
else
|
122
|
+
drives.select { |d| config.drive_ids.include?(d.id) }
|
123
|
+
end
|
124
|
+
|
125
|
+
log_debug("Found drives to index with ids: #{value.map(&:id).join(', ')}")
|
126
|
+
|
127
|
+
value
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def existing_drive_item_ids
|
132
|
+
@existing_drive_item_ids ||= Set.new.tap do |ids|
|
133
|
+
drives_to_index.each do |drive|
|
134
|
+
client.list_items(drive.id) do |item|
|
135
|
+
ids << convert_id_to_fp_id(item.id)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def adapter
|
142
|
+
raise NotImplementedError
|
143
|
+
end
|
144
|
+
|
145
|
+
def convert_id_to_fp_id(_id)
|
146
|
+
raise NotImplementedError
|
147
|
+
end
|
148
|
+
|
149
|
+
def capture_exception(office365_client_error)
|
150
|
+
options = {
|
151
|
+
:extra => {
|
152
|
+
:status_code => office365_client_error.status_code,
|
153
|
+
:endpoint => office365_client_error.endpoint
|
154
|
+
}
|
155
|
+
}
|
156
|
+
ConnectorsShared::ExceptionTracking.capture_exception(office365_client_error, options)
|
157
|
+
end
|
158
|
+
|
159
|
+
def yield_drive_items(drive_id, drive_owner_name:, drive_name:, break_after_page: false, &block)
|
160
|
+
client.list_items(drive_id, break_after_page: break_after_page) do |item|
|
161
|
+
yield_single_document_change(:identifier => "Office365 change: #{item&.id} (#{Office365::Adapter::GraphItem.get_path(item)})") do
|
162
|
+
item.drive_owner_name = drive_owner_name
|
163
|
+
item.drive_name = drive_name
|
164
|
+
yield_create_or_update(drive_id, item, &block)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def yield_correct_actions_and_converted_item(drive_id, item, &block)
|
170
|
+
if item.deleted.nil?
|
171
|
+
yield_create_or_update(drive_id, item, &block)
|
172
|
+
else
|
173
|
+
yield :delete, convert_id_to_fp_id(item.id)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def yield_changes(drive_id, drive_owner_name:, drive_name:, start_delta_link: nil, last_modified: nil, break_after_page: false, &block)
|
178
|
+
client.list_changes(:drive_id => drive_id, :start_delta_link => start_delta_link, :last_modified => last_modified, :break_after_page => break_after_page) do |item|
|
179
|
+
yield_single_document_change(:identifier => "Office365 change: #{item&.id} (#{Office365::Adapter::GraphItem.get_path(item)})") do
|
180
|
+
item.drive_owner_name = drive_owner_name
|
181
|
+
item.drive_name = drive_name
|
182
|
+
yield_correct_actions_and_converted_item(drive_id, item, &block)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def yield_create_or_update(drive_id, item)
|
188
|
+
item = with_permissions(drive_id, item)
|
189
|
+
|
190
|
+
document = generate_document(item)
|
191
|
+
download_args =
|
192
|
+
if downloadable?(item)
|
193
|
+
download_args_and_proc(
|
194
|
+
id: document.fetch(:id),
|
195
|
+
name: item.name,
|
196
|
+
size: item[:size],
|
197
|
+
download_args: { :download_url => item.fetch('@microsoft.graph.downloadUrl') }
|
198
|
+
) do |args|
|
199
|
+
download(args)
|
200
|
+
end
|
201
|
+
else
|
202
|
+
[]
|
203
|
+
end
|
204
|
+
yield :create_or_update, document, download_args
|
205
|
+
end
|
206
|
+
|
207
|
+
def downloadable?(item)
|
208
|
+
item.key?('@microsoft.graph.downloadUrl')
|
209
|
+
end
|
210
|
+
|
211
|
+
def generate_document(item)
|
212
|
+
if item.file
|
213
|
+
adapter.swiftype_document_from_file(item)
|
214
|
+
elsif item.folder
|
215
|
+
adapter.swiftype_document_from_folder(item)
|
216
|
+
elsif item.package
|
217
|
+
adapter.swiftype_document_from_package(item)
|
218
|
+
else
|
219
|
+
raise "Unexpected Office 365 item type for item #{item}"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def with_permissions(drive_id, item)
|
224
|
+
item = item.dup
|
225
|
+
item.permissions = client.item_permissions(drive_id, item.id) if config.index_permissions
|
226
|
+
item
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
Binary file
|
@@ -0,0 +1,47 @@
|
|
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/office365/adapter'
|
10
|
+
|
11
|
+
module ConnectorsSdk
|
12
|
+
module SharePoint
|
13
|
+
class Adapter < Office365::Adapter
|
14
|
+
generate_id_helpers :share_point, 'share_point'
|
15
|
+
|
16
|
+
def self.swiftype_document_from_file(file)
|
17
|
+
FileGraphItem.new(file).to_swiftype_document
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.swiftype_document_from_folder(folder)
|
21
|
+
FolderGraphItem.new(folder).to_swiftype_document
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.swiftype_document_from_package(package)
|
25
|
+
PackageGraphItem.new(package).to_swiftype_document
|
26
|
+
end
|
27
|
+
|
28
|
+
class FileGraphItem < Office365::Adapter::FileGraphItem
|
29
|
+
def self.convert_id_to_fp_id(id)
|
30
|
+
ConnectorsSdk::SharePoint::Adapter.share_point_id_to_fp_id(id)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class FolderGraphItem < Office365::Adapter::FolderGraphItem
|
35
|
+
def self.convert_id_to_fp_id(id)
|
36
|
+
ConnectorsSdk::SharePoint::Adapter.share_point_id_to_fp_id(id)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class PackageGraphItem < Office365::Adapter::PackageGraphItem
|
41
|
+
def self.convert_id_to_fp_id(id)
|
42
|
+
ConnectorsSdk::SharePoint::Adapter.share_point_id_to_fp_id(id)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,91 @@
|
|
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 SharePoint
|
16
|
+
class Authorization
|
17
|
+
class << self
|
18
|
+
def authorization_url
|
19
|
+
'https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
|
20
|
+
end
|
21
|
+
|
22
|
+
def token_credential_uri
|
23
|
+
'https://login.microsoftonline.com/common/oauth2/v2.0/token'
|
24
|
+
end
|
25
|
+
|
26
|
+
def authorization_uri(params)
|
27
|
+
missing = missing_fields(params, %w[client_id])
|
28
|
+
unless missing.blank?
|
29
|
+
raise ConnectorsShared::ClientError.new("Missing required fields: #{missing.join(', ')}")
|
30
|
+
end
|
31
|
+
|
32
|
+
params[:response_type] = 'code'
|
33
|
+
params[:additional_parameters] = { :prompt => 'consent' }
|
34
|
+
client = oauth_client(params)
|
35
|
+
client.authorization_uri.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
def access_token(params)
|
39
|
+
missing = missing_fields(params, %w[client_id client_secret code redirect_uri])
|
40
|
+
unless missing.blank?
|
41
|
+
raise ConnectorsShared::ClientError.new("Missing required fields: #{missing.join(', ')}")
|
42
|
+
end
|
43
|
+
|
44
|
+
params[:grant_type] = 'authorization_code'
|
45
|
+
client = oauth_client(params)
|
46
|
+
client.fetch_access_token
|
47
|
+
end
|
48
|
+
|
49
|
+
def refresh(params)
|
50
|
+
missing = missing_fields(params, %w[client_id client_secret refresh_token])
|
51
|
+
unless missing.blank?
|
52
|
+
raise ConnectorsShared::ClientError.new("Missing required fields: #{missing.join(', ')}")
|
53
|
+
end
|
54
|
+
|
55
|
+
params[:grant_type] = 'refresh_token'
|
56
|
+
client = oauth_client(params)
|
57
|
+
client.refresh!
|
58
|
+
rescue StandardError => e
|
59
|
+
ConnectorsShared::ExceptionTracking.log_exception(e)
|
60
|
+
raise ConnectorsShared::TokenRefreshFailedError
|
61
|
+
end
|
62
|
+
|
63
|
+
def oauth_client(params)
|
64
|
+
options = params.merge(
|
65
|
+
:authorization_uri => authorization_url,
|
66
|
+
:token_credential_uri => token_credential_uri,
|
67
|
+
:scope => oauth_scope
|
68
|
+
)
|
69
|
+
options[:state] = JSON.dump(options[:state]) if options[:state]
|
70
|
+
Signet::OAuth2::Client.new(options)
|
71
|
+
end
|
72
|
+
|
73
|
+
def oauth_scope
|
74
|
+
%w[
|
75
|
+
User.ReadBasic.All
|
76
|
+
Group.Read.All
|
77
|
+
Directory.AccessAsUser.All
|
78
|
+
Files.Read
|
79
|
+
Files.Read.All
|
80
|
+
Sites.Read.All
|
81
|
+
offline_access
|
82
|
+
]
|
83
|
+
end
|
84
|
+
|
85
|
+
def missing_fields(params, required = [])
|
86
|
+
Array.wrap(required).select { |field| params[field.to_sym].nil? }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,31 @@
|
|
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/office365/extractor'
|
10
|
+
require 'connectors_sdk/share_point/adapter'
|
11
|
+
|
12
|
+
module ConnectorsSdk
|
13
|
+
module SharePoint
|
14
|
+
class Extractor < ConnectorsSdk::Office365::Extractor
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def convert_id_to_fp_id(id)
|
19
|
+
ConnectorsSdk::SharePoint::Adapter.share_point_id_to_fp_id(id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def adapter
|
23
|
+
ConnectorsSdk::SharePoint::Adapter
|
24
|
+
end
|
25
|
+
|
26
|
+
def drives
|
27
|
+
client.share_point_drives(:fields => %w(id owner name driveType)).sort_by(&:id)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,117 @@
|
|
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/office365/config'
|
10
|
+
require 'connectors_sdk/share_point/extractor'
|
11
|
+
require 'connectors_sdk/share_point/authorization'
|
12
|
+
require 'bson'
|
13
|
+
|
14
|
+
module ConnectorsSdk
|
15
|
+
module SharePoint
|
16
|
+
SERVICE_TYPE = 'share_point'
|
17
|
+
|
18
|
+
class HttpCallWrapper
|
19
|
+
def extractor(params)
|
20
|
+
cursors = params.fetch(:cursors, {}) || {}
|
21
|
+
features = params.fetch(:features, {}) || {}
|
22
|
+
|
23
|
+
# XXX can we cache that class across calls?
|
24
|
+
ConnectorsSdk::SharePoint::Extractor.new(
|
25
|
+
content_source_id: BSON::ObjectId.new,
|
26
|
+
service_type: SERVICE_TYPE,
|
27
|
+
authorization_data_proc: proc { { access_token: params[:access_token] } },
|
28
|
+
client_proc: proc { ConnectorsSdk::Office365::CustomClient.new(:access_token => params[:access_token], :cursors => cursors) },
|
29
|
+
config: ConnectorsSdk::Office365::Config.new(:cursors => cursors, :drive_ids => 'all', :index_permissions => params[:index_permissions] || false),
|
30
|
+
features: features
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def document_batch(params)
|
35
|
+
results = []
|
36
|
+
|
37
|
+
@extractor = extractor(params)
|
38
|
+
|
39
|
+
@extractor.yield_document_changes(:break_after_page => true, :modified_since => @extractor.config.cursors['modified_since']) do |action, doc, download_args_and_proc|
|
40
|
+
download_obj = nil
|
41
|
+
if download_args_and_proc
|
42
|
+
download_obj = {
|
43
|
+
id: download_args_and_proc[0],
|
44
|
+
name: download_args_and_proc[1],
|
45
|
+
size: download_args_and_proc[2],
|
46
|
+
download_args: download_args_and_proc[3]
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
results << {
|
51
|
+
:action => action,
|
52
|
+
:document => doc,
|
53
|
+
:download => download_obj
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
results
|
58
|
+
rescue ConnectorsSdk::Office365::CustomClient::ClientError => e
|
59
|
+
raise e.status_code == 401 ? ConnectorsShared::InvalidTokenError : e
|
60
|
+
end
|
61
|
+
|
62
|
+
def cursors
|
63
|
+
@extractor.config.cursors
|
64
|
+
end
|
65
|
+
|
66
|
+
def completed?
|
67
|
+
@extractor.completed
|
68
|
+
end
|
69
|
+
|
70
|
+
def deleted(params)
|
71
|
+
results = []
|
72
|
+
extractor(params).yield_deleted_ids(params[:ids]) do |id|
|
73
|
+
results << id
|
74
|
+
end
|
75
|
+
results
|
76
|
+
rescue ConnectorsSdk::Office365::CustomClient::ClientError => e
|
77
|
+
raise e.status_code == 401 ? ConnectorsShared::InvalidTokenError : e
|
78
|
+
end
|
79
|
+
|
80
|
+
def permissions(params)
|
81
|
+
extractor(params).yield_permissions(params[:user_id]) do |permissions|
|
82
|
+
return permissions
|
83
|
+
end
|
84
|
+
rescue ConnectorsSdk::Office365::CustomClient::ClientError => e
|
85
|
+
raise e.status_code == 401 ? ConnectorsShared::InvalidTokenError : e
|
86
|
+
end
|
87
|
+
|
88
|
+
def authorization_uri(body)
|
89
|
+
ConnectorsSdk::SharePoint::Authorization.authorization_uri(body)
|
90
|
+
end
|
91
|
+
|
92
|
+
def access_token(params)
|
93
|
+
ConnectorsSdk::SharePoint::Authorization.access_token(params)
|
94
|
+
end
|
95
|
+
|
96
|
+
def refresh(params)
|
97
|
+
ConnectorsSdk::SharePoint::Authorization.refresh(params)
|
98
|
+
end
|
99
|
+
|
100
|
+
def download(params)
|
101
|
+
extractor(params).download(params[:meta])
|
102
|
+
end
|
103
|
+
|
104
|
+
def name
|
105
|
+
'SharePoint'
|
106
|
+
end
|
107
|
+
|
108
|
+
def source_status(params)
|
109
|
+
client = ConnectorsSdk::Office365::CustomClient.new(:access_token => params[:access_token])
|
110
|
+
client.me
|
111
|
+
{ :status => 'OK', :statusCode => 200, :message => 'Connected to SharePoint' }
|
112
|
+
rescue StandardError => e
|
113
|
+
{ :status => 'FAILURE', :statusCode => e.is_a?(ConnectorsSdk::Office365::CustomClient::ClientError) ? e.status_code : 500, :message => e.message }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
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
|
+
require 'connectors_shared'
|
8
|
+
|
9
|
+
def required_path(absolute_path)
|
10
|
+
absolute_dir = File.dirname(absolute_path)
|
11
|
+
relative_dir = absolute_dir.sub(/.*lib\/connectors_sdk/, 'connectors_sdk')
|
12
|
+
name = File.basename(absolute_path, '.rb')
|
13
|
+
File.join(relative_dir, name)
|
14
|
+
end
|
15
|
+
|
16
|
+
Dir[File.join(__dir__, 'connectors_sdk/**/*.rb')].each { |f| require required_path(f) }
|
@@ -0,0 +1,14 @@
|
|
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 ConnectorsShared
|
8
|
+
class Constants
|
9
|
+
THUMBNAIL_FIELDS = %w[_thumbnail_80x100 _thumbnail_310x430].freeze
|
10
|
+
SUBEXTRACTOR_RESERVED_FIELDS = %w[_subextracted_as_of _subextracted_version].freeze
|
11
|
+
ALLOW_FIELD = '_allow_permissions'.freeze
|
12
|
+
DENY_FIELD = '_deny_permissions'.freeze
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,126 @@
|
|
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 'active_support/core_ext/string'
|
8
|
+
|
9
|
+
module ConnectorsShared
|
10
|
+
class DocumentError
|
11
|
+
attr_accessor :error_class, :error_message, :stack_trace, :error_id
|
12
|
+
|
13
|
+
def initialize(error_class, error_message, stack_trace, error_id)
|
14
|
+
@error_class = error_class
|
15
|
+
@error_message = error_message
|
16
|
+
@error_id = error_id
|
17
|
+
|
18
|
+
# keywords must be < 32kb, UTF-8 chars can be up to 3 bytes, thus 32k/3 ~= 10k
|
19
|
+
# See https://github.com/elastic/workplace-search-team/issues/1723
|
20
|
+
@stack_trace = stack_trace.truncate(10_000)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_h
|
24
|
+
{
|
25
|
+
'error_class' => error_class,
|
26
|
+
'error_message' => error_message,
|
27
|
+
'stack_trace' => stack_trace,
|
28
|
+
'error_id' => error_id
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class ClientError < StandardError; end
|
34
|
+
class EvictionWithNoProgressError < StandardError; end
|
35
|
+
class EvictionError < StandardError
|
36
|
+
attr_accessor :cursors
|
37
|
+
|
38
|
+
def initialize(message = nil, cursors: nil)
|
39
|
+
super(message)
|
40
|
+
@cursors = cursors
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class SuspendedJobError < StandardError
|
45
|
+
attr_accessor :suspend_until, :cursors
|
46
|
+
|
47
|
+
def initialize(message = nil, suspend_until:, cursors: nil)
|
48
|
+
super(message)
|
49
|
+
@suspend_until = suspend_until
|
50
|
+
@cursors = cursors
|
51
|
+
end
|
52
|
+
end
|
53
|
+
class ThrottlingError < SuspendedJobError; end
|
54
|
+
class TransientServerError < SuspendedJobError; end
|
55
|
+
class UnrecoverableServerError < StandardError; end
|
56
|
+
class TransientSubextractorError < StandardError; end
|
57
|
+
class JobDocumentLimitError < StandardError; end
|
58
|
+
class JobClaimingError < StandardError; end
|
59
|
+
|
60
|
+
class MonitoringError < StandardError
|
61
|
+
attr_accessor :tripped_by
|
62
|
+
|
63
|
+
def initialize(message = nil, tripped_by: nil)
|
64
|
+
super("#{message}#{tripped_by.present? ? " Tripped by - #{tripped_by.class}: #{tripped_by.message}" : ''}")
|
65
|
+
@tripped_by = tripped_by
|
66
|
+
end
|
67
|
+
end
|
68
|
+
class MaxSuccessiveErrorsExceededError < MonitoringError; end
|
69
|
+
class MaxErrorsExceededError < MonitoringError; end
|
70
|
+
class MaxErrorsInWindowExceededError < MonitoringError; end
|
71
|
+
|
72
|
+
class JobSyncNotPossibleYetError < StandardError
|
73
|
+
attr_accessor :sync_will_be_possible_at
|
74
|
+
|
75
|
+
def initialize(message = nil, sync_will_be_possible_at: nil)
|
76
|
+
human_readable_errors = []
|
77
|
+
|
78
|
+
human_readable_errors.push(message) unless message.nil?
|
79
|
+
human_readable_errors.push("Content source was created too recently to schedule jobs, next job scheduling is possible at #{sync_will_be_possible_at}.") unless sync_will_be_possible_at.nil?
|
80
|
+
|
81
|
+
super(human_readable_errors.join(' '))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
class PlatinumLicenseRequiredError < StandardError; end
|
85
|
+
class JobInterruptedError < StandardError; end
|
86
|
+
class JobCannotBeUpdatedError < StandardError; end
|
87
|
+
class SecretInvalidError < StandardError; end
|
88
|
+
class InvalidIndexingConfigurationError < StandardError; end
|
89
|
+
class InvalidTokenError < StandardError; end
|
90
|
+
class TokenRefreshFailedError < StandardError; end
|
91
|
+
class ConnectorNotAvailableError < StandardError; end
|
92
|
+
|
93
|
+
# For when we want to explicitly set a #cause but can't
|
94
|
+
class ExplicitlyCausedError < StandardError
|
95
|
+
attr_reader :reason
|
96
|
+
|
97
|
+
def initialize(reason)
|
98
|
+
@reason = reason
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class PublishingFailedError < ExplicitlyCausedError; end
|
103
|
+
|
104
|
+
class Error
|
105
|
+
attr_reader :status_code, :code, :message
|
106
|
+
|
107
|
+
def initialize(status_code, code, message)
|
108
|
+
@status_code = status_code
|
109
|
+
@code = code
|
110
|
+
@message = message
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_h
|
114
|
+
{
|
115
|
+
'code' => @code,
|
116
|
+
'message' => @message
|
117
|
+
}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
INTERNAL_SERVER_ERROR = ConnectorsShared::Error.new(500, 'INTERNAL_SERVER_ERROR', 'Internal server error')
|
122
|
+
INVALID_API_KEY = ConnectorsShared::Error.new(401, 'INVALID_API_KEY', 'Invalid API key')
|
123
|
+
UNSUPPORTED_AUTH_SCHEME = ConnectorsShared::Error.new(401, 'UNSUPPORTED_AUTH_SCHEME', 'Unsupported authorization scheme')
|
124
|
+
INVALID_ACCESS_TOKEN = ConnectorsShared::Error.new(401, 'INVALID_ACCESS_TOKEN', 'Invalid/expired access token, please refresh the token')
|
125
|
+
TOKEN_REFRESH_ERROR = ConnectorsShared::Error.new(401, 'TOKEN_REFRESH_ERROR', 'Failed to refresh token, please re-authenticate the application')
|
126
|
+
end
|