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.
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