connectors_sdk 8.3.0.0.pre.20220414T060419Z → 8.3.0.0.pre.20220510T144908Z
Sign up to get free protection for your applications and to get access to all the features.
- 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 +0 -1
- 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 +270 -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 +31 -9
- data/lib/connectors_sdk/office365/extractor.rb +8 -8
- data/lib/connectors_sdk/share_point/adapter.rb +12 -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/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/stubs/enterprise_search/exception_tracking.rb +43 -0
- metadata +22 -10
- 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/share_point/.http_call_wrapper.rb.un~ +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94b75b9fa3a5f0c46a271a34d073f71977629db6095b6364c8710b0aab92374b
|
4
|
+
data.tar.gz: d133f34052f43e0b8b65ba10eb946c55a6ef421a6296d2d4438e7f4f92b45696
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 712b819efcfa755ce19e4cdb7060dbadefecf37321d07bfe4e7bc9b18254e0f11f5fc29967ff171b1314064d77427bde730ab400a4a2dcde89b31dc6344d4a34
|
7
|
+
data.tar.gz: fa1fedb0c7449b9b2b50b1a56a710178cd3343a108500938244987eb31c694ab0d0214f383df69b6efa6b384eb558697553813c9b93cb142f011b374e05f2c49
|
@@ -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
|
@@ -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 document_batch(params)
|
26
|
+
convert_third_party_errors do
|
27
|
+
results = []
|
28
|
+
|
29
|
+
extractor = extractor(params)
|
30
|
+
|
31
|
+
extractor.yield_document_changes(:break_after_page => true, :modified_since => extractor.config.cursors['modified_since']) do |action, doc, download_args_and_proc|
|
32
|
+
download_obj = nil
|
33
|
+
if download_args_and_proc
|
34
|
+
download_obj = {
|
35
|
+
id: download_args_and_proc[0],
|
36
|
+
name: download_args_and_proc[1],
|
37
|
+
size: download_args_and_proc[2],
|
38
|
+
download_args: download_args_and_proc[3]
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
results << {
|
43
|
+
:action => action,
|
44
|
+
:document => doc,
|
45
|
+
:download => download_obj
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
[results, extractor.config.cursors, extractor.completed]
|
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
|