connectors_sdk 8.3.0.0.pre.20220414T060419Z
Sign up to get free protection for your applications and to get access to all the features.
- 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
|