connectors_sdk 8.3.0.0.pre.20220414T060419Z → 8.3.0.0.pre.20220510T144908Z

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/connectors_sdk/atlassian/config.rb +27 -0
  3. data/lib/connectors_sdk/atlassian/custom_client.rb +87 -0
  4. data/lib/connectors_sdk/base/adapter.rb +7 -8
  5. data/lib/connectors_sdk/base/authorization.rb +89 -0
  6. data/lib/connectors_sdk/base/custom_client.rb +0 -1
  7. data/lib/connectors_sdk/base/extractor.rb +3 -2
  8. data/lib/connectors_sdk/base/http_call_wrapper.rb +135 -0
  9. data/lib/connectors_sdk/base/registry.rb +5 -3
  10. data/lib/connectors_sdk/confluence/adapter.rb +216 -0
  11. data/lib/connectors_sdk/confluence/custom_client.rb +143 -0
  12. data/lib/connectors_sdk/confluence/extractor.rb +270 -0
  13. data/lib/connectors_sdk/confluence_cloud/authorization.rb +64 -0
  14. data/lib/connectors_sdk/confluence_cloud/custom_client.rb +61 -0
  15. data/lib/connectors_sdk/confluence_cloud/extractor.rb +59 -0
  16. data/lib/connectors_sdk/confluence_cloud/http_call_wrapper.rb +59 -0
  17. data/lib/connectors_sdk/helpers/atlassian_time_formatter.rb +10 -0
  18. data/lib/connectors_sdk/office365/adapter.rb +7 -7
  19. data/lib/connectors_sdk/office365/config.rb +1 -0
  20. data/lib/connectors_sdk/office365/custom_client.rb +31 -9
  21. data/lib/connectors_sdk/office365/extractor.rb +8 -8
  22. data/lib/connectors_sdk/share_point/adapter.rb +12 -12
  23. data/lib/connectors_sdk/share_point/authorization.rb +14 -62
  24. data/lib/connectors_sdk/share_point/extractor.rb +2 -2
  25. data/lib/connectors_sdk/share_point/http_call_wrapper.rb +24 -83
  26. data/lib/connectors_shared/exception_tracking.rb +4 -4
  27. data/lib/connectors_shared/extraction_utils.rb +109 -0
  28. data/lib/connectors_shared/middleware/basic_auth.rb +27 -0
  29. data/lib/connectors_shared/middleware/bearer_auth.rb +27 -0
  30. data/lib/connectors_shared/middleware/restrict_hostnames.rb +73 -0
  31. data/lib/connectors_shared/monitor.rb +3 -3
  32. data/lib/stubs/enterprise_search/exception_tracking.rb +43 -0
  33. metadata +22 -10
  34. data/lib/connectors_sdk/base/.config.rb.un~ +0 -0
  35. data/lib/connectors_sdk/base/.connectors.rb.un~ +0 -0
  36. data/lib/connectors_sdk/base/.registry.rb.un~ +0 -0
  37. 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: a44a80ba5fe5032b6254fb8a2db25516542df5818044d627b6ba31f652a7812f
4
- data.tar.gz: 52d1245cc24ab6e1a42c00483ec4a8ece48ede4a6465c9b46b3bf5e18c979678
3
+ metadata.gz: 94b75b9fa3a5f0c46a271a34d073f71977629db6095b6364c8710b0aab92374b
4
+ data.tar.gz: d133f34052f43e0b8b65ba10eb946c55a6ef421a6296d2d4438e7f4f92b45696
5
5
  SHA512:
6
- metadata.gz: 7a4021e1d7b83ec7334913f1ef5e684e02296097f223e96610cb31eb5502c36f9b45b03eeaf80e876aac5bcc531f85d10445917459b86cedc30d6609b33f5351
7
- data.tar.gz: ca04ab72f48ed6e76b3a0e0cc8c260f81779ea906325a782eb61456c9c5be231043d7a73ca9625df63459460de587c65aa352fb2e1150fc8c92a253536fcd634
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}_id_to_fp_id") do |id|
27
+ define_singleton_method("#{method_prefix}_id_to_es_id") do |id|
29
28
  "#{id_prefix}_#{id}"
30
29
  end
31
30
 
32
- define_singleton_method("fp_id_is_#{method_prefix}_id?") do |fp_id|
33
- regex_match = /#{id_prefix}_(.+)$/.match(fp_id)
31
+ define_singleton_method("es_id_is_#{method_prefix}_id?") do |es_id|
32
+ regex_match = /#{id_prefix}_(.+)$/.match(es_id)
34
33
  regex_match.present? && regex_match.size == 2
35
34
  end
36
35
 
37
- define_singleton_method("fp_id_to_#{method_prefix}_id") do |fp_id|
38
- regex_match = /#{id_prefix}_(.+)$/.match(fp_id)
36
+ define_singleton_method("es_id_to_#{method_prefix}_id") do |es_id|
37
+ regex_match = /#{id_prefix}_(.+)$/.match(es_id)
39
38
 
40
- raise ArgumentError, "Invalid id #{fp_id} for source with method prefix #{method_prefix}." if regex_match.nil? || regex_match.length != 2
39
+ raise ArgumentError, "Invalid id #{es_id} for source with method prefix #{method_prefix}." if regex_match.nil? || regex_match.length != 2
41
40
  regex_match[1]
42
41
  end
43
42
  end
@@ -92,7 +91,7 @@ module ConnectorsSdk
92
91
  nil
93
92
  end
94
93
 
95
- def self.swiftype_document_from_configured_object_base(object_type:, object:, fields:)
94
+ def self.es_document_from_configured_object_base(object_type:, object:, fields:)
96
95
  object_as_json = object.as_json
97
96
 
98
97
  adapted_object = {
@@ -0,0 +1,89 @@
1
+ #
2
+ # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ # or more contributor license agreements. Licensed under the Elastic License;
4
+ # you may not use this file except in compliance with the Elastic License.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require 'connectors_shared'
10
+ require 'signet'
11
+ require 'signet/oauth_2'
12
+ require 'signet/oauth_2/client'
13
+
14
+ module ConnectorsSdk
15
+ module Base
16
+ class Authorization
17
+ class << self
18
+ def authorization_uri(params)
19
+ missing = missing_fields(params, %w[client_id])
20
+ unless missing.blank?
21
+ raise ConnectorsShared::ClientError.new("Missing required fields: #{missing.join(', ')}")
22
+ end
23
+
24
+ params[:response_type] = 'code'
25
+ params[:additional_parameters] = additional_parameters
26
+ client = oauth_client(params)
27
+ client.authorization_uri.to_s
28
+ end
29
+
30
+ def access_token(params)
31
+ missing = missing_fields(params, %w[client_id client_secret code redirect_uri])
32
+ unless missing.blank?
33
+ raise ConnectorsShared::ClientError.new("Missing required fields: #{missing.join(', ')}")
34
+ end
35
+
36
+ params[:grant_type] = 'authorization_code'
37
+ client = oauth_client(params)
38
+ client.fetch_access_token
39
+ end
40
+
41
+ def refresh(params)
42
+ missing = missing_fields(params, %w[client_id client_secret refresh_token])
43
+ unless missing.blank?
44
+ raise ConnectorsShared::ClientError.new("Missing required fields: #{missing.join(', ')}")
45
+ end
46
+
47
+ params[:grant_type] = 'refresh_token'
48
+ client = oauth_client(params)
49
+ client.refresh!
50
+ rescue StandardError => e
51
+ ConnectorsShared::ExceptionTracking.log_exception(e)
52
+ raise ConnectorsShared::TokenRefreshFailedError
53
+ end
54
+
55
+ def oauth_client(params)
56
+ options = params.merge(
57
+ :authorization_uri => authorization_url,
58
+ :token_credential_uri => token_credential_uri,
59
+ :scope => oauth_scope
60
+ )
61
+ options[:state] = JSON.dump(options[:state]) if options[:state]
62
+ Signet::OAuth2::Client.new(options)
63
+ end
64
+
65
+ def missing_fields(params, required = [])
66
+ Array.wrap(required).select { |field| params[field.to_sym].nil? }
67
+ end
68
+
69
+ def oauth_scope
70
+ raise 'Not implemented for this connector'
71
+ end
72
+
73
+ private
74
+
75
+ def authorization_url
76
+ raise 'Not implemented for this connector'
77
+ end
78
+
79
+ def token_credential_uri
80
+ raise 'Not implemented for this connector'
81
+ end
82
+
83
+ def additional_parameters
84
+ raise 'Not implemented for this connector'
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -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
@@ -9,10 +9,11 @@ require 'httpclient'
9
9
  require 'active_support/core_ext/array/wrap'
10
10
  require 'active_support/core_ext/numeric/time'
11
11
  require 'active_support/core_ext/object/deep_dup'
12
- require 'connectors_shared'
13
12
  require 'date'
14
- require 'active_support/all'
13
+
14
+ require 'connectors_shared'
15
15
  require 'stubs/connectors/stats' unless defined?(Rails)
16
+ require 'stubs/connectors' unless defined?(Rails)
16
17
 
17
18
  module ConnectorsSdk
18
19
  module Base
@@ -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 connector(name)
21
- @connectors[name].new
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