connectors_sdk 8.2.0.0 → 8.3.0.0.pre.20220517T144653Z
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 +4 -4
- data/lib/connectors_sdk/atlassian/config.rb +27 -0
- data/lib/connectors_sdk/atlassian/custom_client.rb +87 -0
- data/lib/connectors_sdk/base/adapter.rb +7 -8
- data/lib/connectors_sdk/base/authorization.rb +89 -0
- data/lib/connectors_sdk/base/custom_client.rb +1 -2
- data/lib/connectors_sdk/base/extractor.rb +3 -2
- data/lib/connectors_sdk/base/http_call_wrapper.rb +135 -0
- data/lib/connectors_sdk/base/registry.rb +5 -3
- data/lib/connectors_sdk/confluence/adapter.rb +216 -0
- data/lib/connectors_sdk/confluence/custom_client.rb +143 -0
- data/lib/connectors_sdk/confluence/extractor.rb +265 -0
- data/lib/connectors_sdk/confluence_cloud/authorization.rb +64 -0
- data/lib/connectors_sdk/confluence_cloud/custom_client.rb +61 -0
- data/lib/connectors_sdk/confluence_cloud/extractor.rb +59 -0
- data/lib/connectors_sdk/confluence_cloud/http_call_wrapper.rb +59 -0
- data/lib/connectors_sdk/helpers/atlassian_time_formatter.rb +10 -0
- data/lib/connectors_sdk/office365/adapter.rb +7 -7
- data/lib/connectors_sdk/office365/config.rb +1 -0
- data/lib/connectors_sdk/office365/custom_client.rb +25 -64
- data/lib/connectors_sdk/office365/extractor.rb +18 -34
- data/lib/connectors_sdk/share_point/adapter.rb +24 -12
- data/lib/connectors_sdk/share_point/authorization.rb +14 -62
- data/lib/connectors_sdk/share_point/extractor.rb +2 -2
- data/lib/connectors_sdk/share_point/http_call_wrapper.rb +24 -83
- data/lib/connectors_shared/exception_tracking.rb +4 -4
- data/lib/connectors_shared/extraction_utils.rb +109 -0
- data/lib/connectors_shared/job_status.rb +18 -0
- data/lib/connectors_shared/middleware/basic_auth.rb +27 -0
- data/lib/connectors_shared/middleware/bearer_auth.rb +27 -0
- data/lib/connectors_shared/middleware/restrict_hostnames.rb +73 -0
- data/lib/connectors_shared/monitor.rb +3 -3
- data/lib/connectors_shared.rb +1 -0
- data/lib/stubs/enterprise_search/exception_tracking.rb +43 -0
- metadata +23 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b86ca5e489e3cef3b9f2c04a462baf71d6b43805731b0cb52ba2f56f5078d6d3
|
4
|
+
data.tar.gz: 044e860f11163e82c63f66276c3d5628b761c5dfcc5168fc8f80b63ca87d19f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 528fa5260cf80a3ebb918478e1be2e7cac1668588853ad72b1259095086090c15a0cbc028523b1de8b775b3b64ed7427c6fab7ffccc562d559c8164456b84c4b
|
7
|
+
data.tar.gz: a58d353e2b48ffda33aa287d0fa6bb1400c531aae2794995ff3b644b779426d10bb3efc8735d8e07c353fae64bc0b3b0af58427d989f499210a19244b23c1a35
|
@@ -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}
|
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("
|
33
|
-
regex_match = /#{id_prefix}_(.+)$/.match(
|
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("
|
38
|
-
regex_match = /#{id_prefix}_(.+)$/.match(
|
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 #{
|
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.
|
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
|
@@ -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
|
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
|
-
|
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
|
@@ -0,0 +1,135 @@
|
|
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 HttpCallWrapper
|
14
|
+
def extractor(params)
|
15
|
+
extractor_class.new(
|
16
|
+
content_source_id: params[:content_source_id] || "GENERATED-#{BSON::ObjectId.new}",
|
17
|
+
service_type: service_type,
|
18
|
+
authorization_data_proc: proc { { access_token: params[:access_token] } },
|
19
|
+
client_proc: proc { client(params) },
|
20
|
+
config: config(params),
|
21
|
+
features: params.fetch(:features, {}) || {}
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def extract(params)
|
26
|
+
convert_third_party_errors do
|
27
|
+
extractor = extractor(params)
|
28
|
+
|
29
|
+
extractor.yield_document_changes(:modified_since => extractor.config.cursors[:modified_since]) do |action, doc, download_args_and_proc|
|
30
|
+
download_obj = nil
|
31
|
+
if download_args_and_proc
|
32
|
+
download_obj = {
|
33
|
+
id: download_args_and_proc[0],
|
34
|
+
name: download_args_and_proc[1],
|
35
|
+
size: download_args_and_proc[2],
|
36
|
+
download_args: download_args_and_proc[3]
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
doc = {
|
41
|
+
:action => action,
|
42
|
+
:document => doc,
|
43
|
+
:download => download_obj
|
44
|
+
}
|
45
|
+
|
46
|
+
yield doc
|
47
|
+
end
|
48
|
+
|
49
|
+
extractor.config.to_h[:cursors]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def deleted(params)
|
54
|
+
convert_third_party_errors do
|
55
|
+
results = []
|
56
|
+
extractor(params).yield_deleted_ids(params[:ids]) do |id|
|
57
|
+
results << id
|
58
|
+
end
|
59
|
+
results
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def permissions(params)
|
64
|
+
convert_third_party_errors do
|
65
|
+
extractor(params).yield_permissions(params[:user_id]) do |permissions|
|
66
|
+
return permissions
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def authorization_uri(params)
|
72
|
+
authorization.authorization_uri(params)
|
73
|
+
end
|
74
|
+
|
75
|
+
def access_token(params)
|
76
|
+
authorization.access_token(params)
|
77
|
+
end
|
78
|
+
|
79
|
+
def refresh(params)
|
80
|
+
authorization.refresh(params)
|
81
|
+
end
|
82
|
+
|
83
|
+
def download(params)
|
84
|
+
extractor(params).download(params[:meta])
|
85
|
+
end
|
86
|
+
|
87
|
+
def source_status(params)
|
88
|
+
health_check(params)
|
89
|
+
{ :status => 'OK', :statusCode => 200, :message => "Connected to #{name}" }
|
90
|
+
rescue StandardError => e
|
91
|
+
{ :status => 'FAILURE', :statusCode => e.is_a?(custom_client_error) ? e.status_code : 500, :message => e.message }
|
92
|
+
end
|
93
|
+
|
94
|
+
def name
|
95
|
+
raise 'Not implemented for this connector'
|
96
|
+
end
|
97
|
+
|
98
|
+
def service_type
|
99
|
+
self.class::SERVICE_TYPE
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def convert_third_party_errors
|
105
|
+
yield
|
106
|
+
rescue custom_client_error => e
|
107
|
+
raise e.status_code == 401 ? ConnectorsShared::InvalidTokenError : e
|
108
|
+
end
|
109
|
+
|
110
|
+
def extractor_class
|
111
|
+
raise 'Not implemented for this connector'
|
112
|
+
end
|
113
|
+
|
114
|
+
def authorization
|
115
|
+
raise 'Not implemented for this connector'
|
116
|
+
end
|
117
|
+
|
118
|
+
def client(*)
|
119
|
+
raise 'Not implemented for this connector'
|
120
|
+
end
|
121
|
+
|
122
|
+
def custom_client_error
|
123
|
+
raise 'Not implemented for this connector'
|
124
|
+
end
|
125
|
+
|
126
|
+
def config(*)
|
127
|
+
raise 'Not implemented for this connector'
|
128
|
+
end
|
129
|
+
|
130
|
+
def health_check(*)
|
131
|
+
raise 'Not implemented for this connector'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -17,8 +17,8 @@ module ConnectorsSdk
|
|
17
17
|
@connectors[name] = klass
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
@connectors[name]
|
20
|
+
def connector_class(name)
|
21
|
+
@connectors[name]
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -26,7 +26,9 @@ module ConnectorsSdk
|
|
26
26
|
|
27
27
|
# loading plugins (might replace this with a directory scan and conventions on names)
|
28
28
|
require_relative '../share_point/http_call_wrapper'
|
29
|
+
require_relative '../confluence_cloud//http_call_wrapper'
|
29
30
|
|
30
|
-
REGISTRY.register(ConnectorsSdk::SharePoint::SERVICE_TYPE, ConnectorsSdk::SharePoint::HttpCallWrapper)
|
31
|
+
REGISTRY.register(ConnectorsSdk::SharePoint::HttpCallWrapper::SERVICE_TYPE, ConnectorsSdk::SharePoint::HttpCallWrapper)
|
32
|
+
REGISTRY.register(ConnectorsSdk::ConfluenceCloud::HttpCallWrapper::SERVICE_TYPE, ConnectorsSdk::ConfluenceCloud::HttpCallWrapper)
|
31
33
|
end
|
32
34
|
end
|
@@ -0,0 +1,216 @@
|
|
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/adapter'
|
10
|
+
require 'connectors_shared/extraction_utils'
|
11
|
+
require 'nokogiri'
|
12
|
+
|
13
|
+
module ConnectorsSdk
|
14
|
+
module Confluence
|
15
|
+
class Adapter < ConnectorsSdk::Base::Adapter
|
16
|
+
|
17
|
+
MAX_CONTENT_COMMENTS_TO_INDEX = 50
|
18
|
+
LEADING_SLASH_REGEXP = /\A\//
|
19
|
+
|
20
|
+
generate_id_helpers :confluence_space, 'confluence_space'
|
21
|
+
generate_id_helpers :confluence_content, 'confluence_content'
|
22
|
+
generate_id_helpers :confluence_attachment, 'confluence_attachment'
|
23
|
+
|
24
|
+
def self.es_document_from_confluence_space(space, base_url, permissions = [])
|
25
|
+
SpaceNode.new(:node => space, :base_url => base_url, :permissions => permissions).to_es_document
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.es_document_from_confluence_content(content, base_url, restrictions = [])
|
29
|
+
ContentNode.new(:node => content, :base_url => base_url, :permissions => restrictions).to_es_document
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.es_document_from_confluence_attachment(attachment, base_url, restrictions = [])
|
33
|
+
AttachmentNode.new(:node => attachment, :base_url => base_url, :permissions => restrictions).to_es_document
|
34
|
+
end
|
35
|
+
|
36
|
+
class Node
|
37
|
+
attr_reader :node, :base_url, :permissions
|
38
|
+
|
39
|
+
def initialize(node:, base_url:, permissions: [])
|
40
|
+
@node = node
|
41
|
+
@base_url = base_url
|
42
|
+
@base_url = "#{base_url}/" unless @base_url.ends_with?('/')
|
43
|
+
@permissions = permissions
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_es_document
|
47
|
+
{
|
48
|
+
:id => id,
|
49
|
+
:title => title,
|
50
|
+
:url => url,
|
51
|
+
:type => ConnectorsSdk::Base::Adapter.normalize_enum(type),
|
52
|
+
}.merge(fields)
|
53
|
+
end
|
54
|
+
|
55
|
+
def id
|
56
|
+
raise NotImplementedError
|
57
|
+
end
|
58
|
+
|
59
|
+
def type
|
60
|
+
raise NotImplementedError
|
61
|
+
end
|
62
|
+
|
63
|
+
def title
|
64
|
+
raise NotImplementedError
|
65
|
+
end
|
66
|
+
|
67
|
+
def url
|
68
|
+
raise NotImplementedError
|
69
|
+
end
|
70
|
+
|
71
|
+
def fields
|
72
|
+
{}
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
def permissions_hash
|
78
|
+
permissions.blank? ? {} : { ConnectorsShared::Constants::ALLOW_FIELD => permissions }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class SpaceNode < Node
|
83
|
+
def id
|
84
|
+
Confluence::Adapter.confluence_space_id_to_es_id(node.fetch('key'))
|
85
|
+
end
|
86
|
+
|
87
|
+
def type
|
88
|
+
'space'
|
89
|
+
end
|
90
|
+
|
91
|
+
def title
|
92
|
+
node.name
|
93
|
+
end
|
94
|
+
|
95
|
+
def url
|
96
|
+
Addressable::URI.join(base_url, (node._links.webui || node._links.self).gsub(LEADING_SLASH_REGEXP, '')).to_s
|
97
|
+
end
|
98
|
+
|
99
|
+
def path
|
100
|
+
title
|
101
|
+
end
|
102
|
+
|
103
|
+
def fields
|
104
|
+
permissions_hash
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class ContentNode < Node
|
109
|
+
def id
|
110
|
+
Confluence::Adapter.confluence_content_id_to_es_id(node.id)
|
111
|
+
end
|
112
|
+
|
113
|
+
def type
|
114
|
+
case node.type
|
115
|
+
when 'page'
|
116
|
+
node.type
|
117
|
+
when 'blogpost'
|
118
|
+
'blog post'
|
119
|
+
else
|
120
|
+
ConnectorsShared::ExceptionTracking.capture_message("Unknown confluence type: #{node.type}")
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def title
|
126
|
+
node.title
|
127
|
+
end
|
128
|
+
|
129
|
+
def url
|
130
|
+
Addressable::URI.join(base_url, node._links.webui.gsub(LEADING_SLASH_REGEXP, '')).to_s
|
131
|
+
end
|
132
|
+
|
133
|
+
def body
|
134
|
+
text_from_html(node.body.export_view.value)
|
135
|
+
end
|
136
|
+
|
137
|
+
def comments
|
138
|
+
node.children&.comment&.results&.slice(0, MAX_CONTENT_COMMENTS_TO_INDEX)&.map do |comment|
|
139
|
+
text_from_html(comment.body.export_view.value)
|
140
|
+
end&.join("\n")
|
141
|
+
end
|
142
|
+
|
143
|
+
def description
|
144
|
+
[
|
145
|
+
node.space&.name,
|
146
|
+
node.ancestors&.map(&:title) # 'attachment' type nodes do not have `ancestors`, making this logic incomplete
|
147
|
+
].flatten.select(&:present?).join('/')
|
148
|
+
end
|
149
|
+
|
150
|
+
def path
|
151
|
+
[
|
152
|
+
description,
|
153
|
+
title
|
154
|
+
].select(&:present?).join('/')
|
155
|
+
end
|
156
|
+
|
157
|
+
def fields
|
158
|
+
{
|
159
|
+
:description => description,
|
160
|
+
:body => body,
|
161
|
+
:comments => comments,
|
162
|
+
:created_by => node.history&.createdBy&.displayName,
|
163
|
+
:project => node.space.try!(:[], :key),
|
164
|
+
|
165
|
+
:created_at => ConnectorsSdk::Base::Adapter.normalize_date(node.history&.createdDate),
|
166
|
+
:last_updated => ConnectorsSdk::Base::Adapter.normalize_date(node.history&.lastUpdated&.when)
|
167
|
+
}.merge(permissions_hash)
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def text_from_html(raw_html)
|
173
|
+
ConnectorsShared::ExtractionUtils.node_descendant_text(Nokogiri::HTML(raw_html))
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class AttachmentNode < ContentNode
|
178
|
+
def id
|
179
|
+
Confluence::Adapter.confluence_attachment_id_to_es_id(node.id)
|
180
|
+
end
|
181
|
+
|
182
|
+
def type
|
183
|
+
'attachment'
|
184
|
+
end
|
185
|
+
|
186
|
+
def fields
|
187
|
+
mime_type = [
|
188
|
+
node.extensions.mediaType,
|
189
|
+
ConnectorsSdk::Base::Adapter.mime_type_for_file(node.title)
|
190
|
+
].detect(&:present?)
|
191
|
+
extension = ConnectorsSdk::Base::Adapter.extension_for_file(node.title)
|
192
|
+
|
193
|
+
{
|
194
|
+
:size => node.extensions.fileSize,
|
195
|
+
:container => node&.container&.title,
|
196
|
+
|
197
|
+
:description => description,
|
198
|
+
:comments => comments,
|
199
|
+
:created_by => node.history&.createdBy&.displayName,
|
200
|
+
:project => node.space.try!(:[], :key),
|
201
|
+
|
202
|
+
:created_at => ConnectorsSdk::Base::Adapter.normalize_date(node.history&.createdDate),
|
203
|
+
:last_updated => ConnectorsSdk::Base::Adapter.normalize_date(node.history&.lastUpdated&.when)
|
204
|
+
}.merge(permissions_hash).tap do |data|
|
205
|
+
data[:mime_type] = mime_type if mime_type.present?
|
206
|
+
data[:extension] = extension if extension.present?
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def to_es_document
|
211
|
+
super.merge(:_fields_to_preserve => ConnectorsSdk::Confluence::Adapter.fields_to_preserve)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|