4me-sdk 1.2.0 → 2.0.0.pre.rc.1

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.
@@ -8,7 +8,7 @@ require 'mime/types'
8
8
  # Created:: 22 Feb 2008
9
9
  module Sdk4me
10
10
  module Multipart
11
- VERSION = "1.0.0" unless const_defined?(:VERSION)
11
+ VERSION = '1.0.0'.freeze unless const_defined?(:VERSION)
12
12
 
13
13
  # Formats a given hash as a multipart form post
14
14
  # If a hash value responds to :string or :read messages, then it is
@@ -16,10 +16,10 @@ module Sdk4me
16
16
  # to be a string
17
17
  class Post
18
18
  # We have to pretend like we're a web browser...
19
- USERAGENT = "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6" unless const_defined?(:USERAGENT)
20
- BOUNDARY = '0123456789ABLEWASIEREISAWELBA9876543210' unless const_defined?(:BOUNDARY)
21
- CONTENT_TYPE = "multipart/form-data; boundary=#{ BOUNDARY }" unless const_defined?(:CONTENT_TYPE)
22
- HEADER = { 'Content-Type' => CONTENT_TYPE, 'User-Agent' => USERAGENT } unless const_defined?(:HEADER)
19
+ USERAGENT = "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6 4me/#{Sdk4me::Client::VERSION}".freeze unless const_defined?(:USERAGENT)
20
+ BOUNDARY = '0123456789ABLEWASIEREISAWELBA9876543210'.freeze unless const_defined?(:BOUNDARY)
21
+ CONTENT_TYPE = "multipart/form-data; boundary=#{BOUNDARY}".freeze unless const_defined?(:CONTENT_TYPE)
22
+ HEADER = { 'Content-Type' => CONTENT_TYPE, 'User-Agent' => USERAGENT }.freeze unless const_defined?(:HEADER)
23
23
 
24
24
  def self.prepare_query(params)
25
25
  fp = []
@@ -33,24 +33,22 @@ module Sdk4me
33
33
  end
34
34
 
35
35
  # Assemble the request body using the special multipart format
36
- query = fp.map{ |p| "--#{BOUNDARY}\r\n#{p.to_multipart}" }.join + "--#{BOUNDARY}--"
37
- return query, HEADER
36
+ query = fp.map { |p| "--#{BOUNDARY}\r\n#{p.to_multipart}" }.join + "--#{BOUNDARY}--"
37
+ [query, HEADER]
38
38
  end
39
39
  end
40
40
 
41
- private
42
-
43
41
  # Formats a basic string key/value pair for inclusion with a multipart post
44
42
  class StringParam
45
43
  attr_accessor :k, :v
46
44
 
47
- def initialize(k, v)
48
- @k = k
49
- @v = v
45
+ def initialize(key, value)
46
+ @k = key
47
+ @v = value
50
48
  end
51
49
 
52
50
  def to_multipart
53
- return %(Content-Disposition: form-data; name="#{CGI::escape(k.to_s)}"\r\n\r\n#{v}\r\n)
51
+ %(Content-Disposition: form-data; name="#{CGI.escape(k.to_s)}"\r\n\r\n#{v}\r\n)
54
52
  end
55
53
  end
56
54
 
@@ -59,17 +57,17 @@ module Sdk4me
59
57
  class FileParam
60
58
  attr_accessor :k, :filename, :content
61
59
 
62
- def initialize(k, filename, content)
63
- @k = k
60
+ def initialize(key, filename, content)
61
+ @k = key
64
62
  @filename = filename
65
63
  @content = content
66
64
  end
67
65
 
68
66
  def to_multipart
69
67
  # If we can tell the possible mime-type from the filename, use the first in the list; otherwise, use "application/octet-stream"
70
- mime_type = MIME::Types.type_for(filename)[0] || MIME::Types["application/octet-stream"][0]
71
- return %(Content-Disposition: form-data; name="#{CGI::escape(k.to_s)}"; filename="#{filename}"\r\nContent-Type: #{ mime_type.simplified }\r\n\r\n#{ content }\r\n)
68
+ mime_type = MIME::Types.type_for(filename)[0] || MIME::Types['application/octet-stream'][0]
69
+ %(Content-Disposition: form-data; name="#{CGI.escape(k.to_s)}"; filename="#{filename}"\r\nContent-Type: #{mime_type.simplified}\r\n\r\n#{content}\r\n)
72
70
  end
73
71
  end
74
72
  end
75
- end
73
+ end
@@ -1,19 +1,13 @@
1
1
  module Sdk4me
2
2
  class Response
3
+ attr_reader :request, :response
4
+ alias raw response
5
+
3
6
  def initialize(request, response)
4
7
  @request = request
5
8
  @response = response
6
9
  end
7
10
 
8
- def request
9
- @request
10
- end
11
-
12
- def response
13
- @response
14
- end
15
- alias_method :raw, :response
16
-
17
11
  def body
18
12
  @response.body
19
13
  end
@@ -22,16 +16,17 @@ module Sdk4me
22
16
  # If the response is not +valid?+ it is a Hash with 'message' and optionally 'errors'
23
17
  def json
24
18
  return @json if defined?(@json)
19
+
25
20
  # no content, no JSON
26
21
  if @response.code.to_s == '204'
27
22
  data = {}
28
23
  elsif @response.body.blank?
29
24
  # no body, no json
30
- data = {message: @response.message.blank? ? 'empty body' : @response.message.strip}
25
+ data = { message: @response.message.blank? ? 'empty body' : @response.message.strip }
31
26
  end
32
27
  begin
33
28
  data ||= JSON.parse(@response.body)
34
- rescue ::Exception => e
29
+ rescue StandardError => e
35
30
  data = { message: "Invalid JSON - #{e.message} for:\n#{@response.body}" }
36
31
  end
37
32
  # indifferent access to hashes
@@ -57,7 +52,7 @@ module Sdk4me
57
52
  def valid?
58
53
  message.nil?
59
54
  end
60
- alias_method :success?, :valid?
55
+ alias success? valid?
61
56
 
62
57
  # +true+ in case of a HTTP 5xx error
63
58
  def failure?
@@ -69,15 +64,19 @@ module Sdk4me
69
64
  # @param keys: a single key or a key-path separated by comma
70
65
  def[](*keys)
71
66
  values = json.is_a?(Array) ? json : [json]
72
- keys.each { |key| values = values.map{ |value| value.is_a?(Hash) ? value[key] : nil} }
67
+ keys.each { |key| values = values.map { |value| value.is_a?(Hash) ? value[key] : nil } }
73
68
  json.is_a?(Array) ? values : values.first
74
69
  end
75
70
 
76
71
  # The nr of resources found
77
72
  def size
78
- @size ||= message ? 0 : json.is_a?(Array) ? json.size : 1
73
+ @size ||= if message
74
+ 0
75
+ else
76
+ json.is_a?(Array) ? json.size : 1
77
+ end
79
78
  end
80
- alias :count :size
79
+ alias count size
81
80
 
82
81
  # pagination - per page
83
82
  def per_page
@@ -103,12 +102,12 @@ module Sdk4me
103
102
  # Link: <https://api.4me.com/v1/requests?page=1&per_page=25>; rel="first", <https://api.4me.com/v1/requests?page=2&per_page=25>; rel="prev", etc.
104
103
  def pagination_link(relation)
105
104
  # split on ',' select the [url] in '<[url]>; rel="[relation]"', compact to all url's found (at most one) and take the first
106
- (@pagination_links ||= {})[relation] ||= @response.header['Link'] && @response.header['Link'].split(/,\s*<?/).map{ |link| link[/^\s*<?(.*?)>?;\s*rel="#{relation.to_s}"\s*$/, 1] }.compact.first
105
+ (@pagination_links ||= {})[relation] ||= @response.header['Link'] && @response.header['Link'].split(/,\s*<?/).map { |link| link[/^\s*<?(.*?)>?;\s*rel="#{relation}"\s*$/, 1] }.compact.first
107
106
  end
108
107
 
109
108
  # pagination urls (relative paths without server) - relations :first, :prev, :next, :last
110
109
  def pagination_relative_link(relation)
111
- (@pagination_relative_links ||= {})[relation] ||= pagination_link(relation) && pagination_link(relation)[/^https?:\/\/[^\/]*(.*)/, 1]
110
+ (@pagination_relative_links ||= {})[relation] ||= pagination_link(relation) && pagination_link(relation)[%r{^https?://[^/]*(.*)}, 1]
112
111
  end
113
112
 
114
113
  # +true+ if the response is invalid because of throttling
@@ -117,12 +116,11 @@ module Sdk4me
117
116
  end
118
117
 
119
118
  def retry_after
120
- @current_page ||= @response.header['Retry-After'].to_i
119
+ @retry_after ||= @response.header['Retry-After'].to_i
121
120
  end
122
121
 
123
122
  def to_s
124
123
  valid? ? json.to_s : message
125
124
  end
126
-
127
125
  end
128
126
  end
@@ -1,5 +1,5 @@
1
1
  module Sdk4me
2
2
  class Client
3
- VERSION = '1.2.0'
3
+ VERSION = '2.0.0-rc.1'.freeze
4
4
  end
5
5
  end
@@ -1,262 +1,341 @@
1
1
  require 'spec_helper'
2
+ require 'tempfile'
2
3
 
3
4
  describe Sdk4me::Attachments do
4
-
5
- def attachments(authentication)
6
- (@attachments ||= {})[authentication] ||= begin
7
- client = if authentication == :api_token
8
- Sdk4me::Client.new(api_token: 'secret', max_retry_time: -1)
9
- else
10
- Sdk4me::Client.new(access_token: 'secret', max_retry_time: -1)
11
- end
12
- Sdk4me::Attachments.new(client)
13
- end
5
+ def attachments(authentication, path)
6
+ @client = if authentication == :api_token
7
+ Sdk4me::Client.new(api_token: 'secret', max_retry_time: -1)
8
+ else
9
+ Sdk4me::Client.new(access_token: 'secret', max_retry_time: -1)
10
+ end
11
+ Sdk4me::Attachments.new(@client, path)
14
12
  end
15
13
 
16
14
  def credentials(authentication)
17
15
  if authentication == :api_token
18
- { basic_auth: ['secret', 'x'] }
16
+ { basic_auth: %w[secret x] }
19
17
  else
20
- { headers: {'Authorization' => 'Bearer secret'} }
18
+ { headers: { 'Authorization' => 'Bearer secret' } }
21
19
  end
22
20
  end
23
21
 
24
- [:api_token, :access_token].each do |authentication|
25
-
26
- context "#{authentication} - " do
27
- context 'upload_attachments!' do
28
- context 'normal' do
29
- it 'should not do anything when no :attachments are present' do
30
- expect(attachments(authentication).upload_attachments!('/requests', {status: :in_progress})).to be_nil
22
+ %i[api_token access_token].each do |authentication|
23
+ context "#{authentication} -" do
24
+ context 'upload_attachments! -' do
25
+ context 'field attachments' do
26
+ it 'should not do anything when no attachments are present' do
27
+ a = attachments(authentication, '/requests')
28
+ expect(@client).not_to receive(:send_file)
29
+ a.upload_attachments!({ status: :in_progress })
31
30
  end
32
31
 
33
- it 'should not do anything when :attachments is nil' do
34
- expect(attachments(authentication).upload_attachments!('/requests', {attachments: nil})).to be_nil
32
+ it 'should not do anything when attachments is nil' do
33
+ a = attachments(authentication, '/requests')
34
+ expect(@client).not_to receive(:send_file)
35
+ a.upload_attachments!({ note_attachments: nil })
35
36
  end
36
37
 
37
- it 'should not do anything when :attachments is empty' do
38
- expect(attachments(authentication).upload_attachments!('/requests', {attachments: []})).to be_nil
39
- expect(attachments(authentication).upload_attachments!('/requests', {attachments: [nil]})).to be_nil
38
+ it 'should not do anything when attachments is empty' do
39
+ a = attachments(authentication, '/requests')
40
+ expect(@client).not_to receive(:send_file)
41
+ a.upload_attachments!({ note_attachments: [] })
42
+ a.upload_attachments!({ note_attachments: [nil] })
40
43
  end
41
44
 
42
- it 'should show a error if no attachment may be uploaded' do
43
- stub_request(:get, 'https://api.4me.com/v1/sites/1?attachment_upload_token=true').with(credentials(authentication)).to_return(body: {name: 'site 1'}.to_json)
44
- expect_log('Attachments not allowed for /sites/1', :error)
45
- expect(attachments(authentication).upload_attachments!('/sites/1', {attachments: ['file1.png']})).to be_nil
45
+ it 'should raise an error if no attachment provider can be determined' do
46
+ a = attachments(authentication, '/requests')
47
+ expect(@client).not_to receive(:send_file)
48
+ stub_request(:get, 'https://api.4me.com/v1/attachments/storage').with(credentials(authentication)).to_return(status: 404, body: { message: 'Not Found' }.to_json)
49
+ expect_log('GET request to api.4me.com:443/v1/attachments/storage failed: 404: Not Found', :error)
50
+ expect_log('Attachment upload failed: No provider found', :error)
51
+ expect { a.upload_attachments!({ note_attachments: ['file1.png'] }) }.to raise_error(::Sdk4me::UploadFailed, 'Attachment upload failed: No provider found')
46
52
  end
47
53
 
48
- it 'should raise an exception if no attachment may be uploaded' do
49
- stub_request(:get, 'https://api.4me.com/v1/sites/1?attachment_upload_token=true').with(credentials(authentication)).to_return(body: {name: 'site 1'}.to_json)
50
- message = 'Attachments not allowed for /sites/1'
51
- expect{ attachments(authentication).upload_attachments!('/sites/1', {attachments: ['file1.png'], attachments_exception: true}) }.to raise_error(::Sdk4me::UploadFailed, message)
52
- end
53
-
54
- it 'should add /new to the path for new records' do
55
- stub_request(:get, 'https://api.4me.com/v1/sites/new?attachment_upload_token=true').with(credentials(authentication)).to_return(body: {missing: 'storage'}.to_json)
56
- expect_log('Attachments not allowed for /sites', :error)
57
- expect(attachments(authentication).upload_attachments!('/sites', {attachments: ['file1.png']})).to be_nil
58
- end
54
+ it 'should upload' do
55
+ a = attachments(authentication, '/requests')
56
+ resp = {
57
+ provider: 'local',
58
+ upload_uri: 'https://widget.example.com/attachments',
59
+ local: {
60
+ key: 'attachments/5/requests/000/000/777/abc/${filename}',
61
+ x_4me_expiration: '2020-11-01T23:59:59Z',
62
+ x_4me_signature: 'foobar'
63
+ }
64
+ }
65
+ stub_request(:get, 'https://api.4me.com/v1/attachments/storage').with(credentials(authentication)).to_return(status: 200, body: resp.to_json)
59
66
 
60
- [ [:requests, :note],
61
- [:problems, :note],
62
- [:contracts, :remarks],
63
- [:cis, :remarks],
64
- [:flsas, :remarks],
65
- [:slas, :remarks],
66
- [:service_instances, :remarks],
67
- [:service_offerings, :summary],
68
- [:any_other_model, :note]].each do |model, attribute|
69
-
70
- it "should replace :attachments with :#{attribute}_attachments after upload at /#{model}" do
71
- stub_request(:get, "https://api.4me.com/v1/#{model}/new?attachment_upload_token=true").with(credentials(authentication)).to_return(body: {storage_upload: 'conf'}.to_json)
72
- expect(attachments(authentication)).to receive(:upload_attachment).with('conf', 'file1.png', false).ordered{ 'uploaded file1.png' }
73
- expect(attachments(authentication)).to receive(:upload_attachment).with('conf', 'file2.zip', false).ordered{ 'uploaded file2.zip' }
74
- data = {leave: 'me alone', attachments: %w(file1.png file2.zip)}
75
- attachments(authentication).upload_attachments!("/#{model}", data)
76
- expect(data[:attachments]).to be_nil
77
- expect(data[:leave]).to eq('me alone')
78
- expect(data[:"#{attribute}_attachments"]).to eq(['uploaded file1.png', 'uploaded file2.zip'].to_json)
79
- end
80
- end
67
+ expect(a).to(receive(:upload_attachment).with('/tmp/file1.png').ordered { { key: 'attachments/5/requests/000/000/777/abc/file1.png', filesize: 1234 } })
68
+ expect(a).to(receive(:upload_attachment).with('/tmp/file2.zip').ordered { { key: 'attachments/5/requests/000/000/777/abc/file2.zip', filesize: 9876 } })
81
69
 
82
- it 'should set raise_exception flag to true when :attachments_exception is set' do
83
- stub_request(:get, 'https://api.4me.com/v1/requests/new?attachment_upload_token=true').with(credentials(authentication)).to_return(body: {storage_upload: 'conf'}.to_json)
84
- expect(attachments(authentication)).to receive(:upload_attachment).with('conf', 'file1.png', true).ordered{ 'uploaded file1.png' }
85
- data = {leave: 'me alone', attachments: 'file1.png', attachments_exception: true}
86
- attachments(authentication).upload_attachments!('/requests', data)
87
- expect(data[:attachments]).to be_nil
88
- expect(data[:attachments_exception]).to be_nil
89
- expect(data[:leave]).to eq('me alone')
90
- expect(data[:note_attachments]).to eq(['uploaded file1.png'].to_json)
70
+ data = { subject: 'Foobar', note_attachments: ['/tmp/file1.png', '/tmp/file2.zip'] }
71
+ a.upload_attachments!(data)
72
+ expect(data).to eq({ subject: 'Foobar', note_attachments: [
73
+ { filesize: 1234, key: 'attachments/5/requests/000/000/777/abc/file1.png' },
74
+ { filesize: 9876, key: 'attachments/5/requests/000/000/777/abc/file2.zip' }
75
+ ] })
91
76
  end
92
77
  end
93
78
 
94
- context 'inline' do
95
- it 'should not do anything when no [attachment:...] is present in the note' do
96
- expect(attachments(authentication).upload_attachments!('/requests', {note: '[attachmen:/type]'})).to be_nil
79
+ context 'rich text inline attachments' do
80
+ it 'should not do anything when no [note_attachments: <idx>] is present in the note' do
81
+ a = attachments(authentication, '/requests')
82
+ expect(@client).not_to receive(:send_file)
83
+ data = { note: '[note_attachments: foo]' }
84
+ a.upload_attachments!(data)
85
+ expect(data).to eq({ note: '[note_attachments: foo]' })
97
86
  end
98
87
 
99
- it 'should not do anything when attachment is empty' do
100
- expect(attachments(authentication).upload_attachments!('/requests', {note: '[attachment:]'})).to be_nil
88
+ it 'should not do anything when note attachments is empty' do
89
+ a = attachments(authentication, '/requests')
90
+ expect(@client).not_to receive(:send_file)
91
+ data = { note: '[note_attachments: 0]' }
92
+ a.upload_attachments!(data)
93
+ expect(data).to eq({ note: '[note_attachments: 0]' })
101
94
  end
102
95
 
103
- it 'should show a error if no attachment may be uploaded' do
104
- stub_request(:get, 'https://api.4me.com/v1/sites/1?attachment_upload_token=true').with(credentials(authentication)).to_return(body: {name: 'site 1'}.to_json)
105
- expect_log('Attachments not allowed for /sites/1', :error)
106
- expect(attachments(authentication).upload_attachments!('/sites/1', {note: '[attachment:file1.png]'})).to be_nil
107
- end
108
-
109
- it 'should raise an exception if no attachment may be uploaded' do
110
- stub_request(:get, 'https://api.4me.com/v1/sites/1?attachment_upload_token=true').with(credentials(authentication)).to_return(body: {name: 'site 1'}.to_json)
111
- message = 'Attachments not allowed for /sites/1'
112
- expect{ attachments(authentication).upload_attachments!('/sites/1', {note: '[attachment:file1.png]', attachments_exception: true}) }.to raise_error(::Sdk4me::UploadFailed, message)
96
+ it 'should raise an error if no attachment provider can be determined' do
97
+ a = attachments(authentication, '/requests')
98
+ expect(@client).not_to receive(:send_file)
99
+ stub_request(:get, 'https://api.4me.com/v1/attachments/storage').with(credentials(authentication)).to_return(status: 404, body: { message: 'Not Found' }.to_json)
100
+ expect_log('GET request to api.4me.com:443/v1/attachments/storage failed: 404: Not Found', :error)
101
+ expect_log('Attachment upload failed: No provider found', :error)
102
+ data = {
103
+ note: '[note_attachments: 0]', note_attachments: ['/tmp/doesnotexist.log']
104
+ }
105
+ expect { a.upload_attachments!(data) }.to raise_error(::Sdk4me::UploadFailed, 'Attachment upload failed: No provider found')
113
106
  end
114
107
 
115
- it 'should add /new to the path for new records' do
116
- stub_request(:get, 'https://api.4me.com/v1/sites/new?attachment_upload_token=true').with(credentials(authentication)).to_return(body: {missing: 'storage'}.to_json)
117
- expect_log('Attachments not allowed for /sites', :error)
118
- expect(attachments(authentication).upload_attachments!('/sites', {note: '[attachment:file1.png]'})).to be_nil
119
- end
108
+ it 'should upload' do
109
+ a = attachments(authentication, '/requests')
110
+ resp = {
111
+ provider: 'local',
112
+ upload_uri: 'https://widget.example.com/attachments',
113
+ local: {
114
+ key: 'attachments/5/requests/000/000/777/abc/${filename}',
115
+ x_4me_expiration: '2020-11-01T23:59:59Z',
116
+ x_4me_signature: 'foobar'
117
+ }
118
+ }
119
+ stub_request(:get, 'https://api.4me.com/v1/attachments/storage').with(credentials(authentication)).to_return(status: 200, body: resp.to_json)
120
120
 
121
- [ [:requests, :note],
122
- [:problems, :note],
123
- [:contracts, :remarks],
124
- [:cis, :remarks],
125
- [:flsas, :remarks],
126
- [:slas, :remarks],
127
- [:service_instances, :remarks],
128
- [:service_offerings, :summary],
129
- [:any_other_model, :note]].each do |model, attribute|
130
-
131
- it "should replace :attachments with :#{attribute}_attachments after upload at /#{model}" do
132
- stub_request(:get, "https://api.4me.com/v1/#{model}/new?attachment_upload_token=true").with(credentials(authentication)).to_return(body: {storage_upload: 'conf'}.to_json)
133
- expect(attachments(authentication)).to receive(:upload_attachment).with('conf', 'file1.png', false).ordered{ {key: 'uploaded file1.png'} }
134
- expect(attachments(authentication)).to receive(:upload_attachment).with('conf', 'file2.zip', false).ordered{ {key: 'uploaded file2.zip'} }
135
- data = {leave: 'me alone', attribute => '[attachment:file1.png] and [attachment:file2.zip]'}
136
- attachments(authentication).upload_attachments!("/#{model}", data)
137
- expect(data[:attachments]).to be_nil
138
- expect(data[:leave]).to eq('me alone')
139
- expect(data[:"#{attribute}_attachments"]).to eq([{key: 'uploaded file1.png', inline: true}, {key: 'uploaded file2.zip', inline: true}].to_json)
140
- expect(data[:"#{attribute}"]).to eq('![](uploaded file1.png) and ![](uploaded file2.zip)')
141
- end
142
- end
121
+ expect(a).to(receive(:upload_attachment).with('/tmp/file1.png').ordered { { key: 'attachments/5/requests/000/000/777/abc/file1.png', filesize: 1234 } })
122
+ expect(a).to(receive(:upload_attachment).with('/tmp/file2.jpg').ordered { { key: 'attachments/5/requests/000/000/777/abc/file2.jpg', filesize: 9876 } })
143
123
 
144
- it 'should set raise_exception flag to true when :attachments_exception is set' do
145
- stub_request(:get, 'https://api.4me.com/v1/requests/new?attachment_upload_token=true').with(credentials(authentication)).to_return(body: {storage_upload: 'conf'}.to_json)
146
- expect(attachments(authentication)).to receive(:upload_attachment).with('conf', 'file1.png', true).ordered{ {key: 'uploaded file1.png'} }
147
- data = {leave: 'me alone', note: '[attachment:file1.png]', attachments_exception: true}
148
- attachments(authentication).upload_attachments!('/requests', data)
149
- expect(data[:attachments]).to be_nil
150
- expect(data[:attachments_exception]).to be_nil
151
- expect(data[:leave]).to eq('me alone')
152
- expect(data[:note_attachments]).to eq([{key: 'uploaded file1.png', inline: true}].to_json)
153
- expect(data[:note]).to eq('![](uploaded file1.png)')
124
+ data = {
125
+ subject: 'Foobar',
126
+ note: 'Foo [note_attachments: 0] Bar [note_attachments: 1]',
127
+ note_attachments: ['/tmp/file1.png', '/tmp/file2.jpg']
128
+ }
129
+ a.upload_attachments!(data)
130
+
131
+ expect(data).to eq({
132
+ note: 'Foo ![](attachments/5/requests/000/000/777/abc/file1.png) Bar ![](attachments/5/requests/000/000/777/abc/file2.jpg)',
133
+ note_attachments: [
134
+ { filesize: 1234, inline: true, key: 'attachments/5/requests/000/000/777/abc/file1.png' },
135
+ { filesize: 9876, inline: true, key: 'attachments/5/requests/000/000/777/abc/file2.jpg' }
136
+ ],
137
+ subject: 'Foobar'
138
+ })
154
139
  end
155
140
  end
156
141
 
157
- end
158
-
159
- context 'upload_attachment' do
142
+ context 'field attachments and rich text inline attachments' do
143
+ it 'should upload, and replace the data in place' do
144
+ a = attachments(authentication, '/requests')
145
+ resp = {
146
+ provider: 'local',
147
+ upload_uri: 'https://widget.example.com/attachments',
148
+ local: {
149
+ key: 'attachments/5/requests/000/000/777/abc/${filename}',
150
+ x_4me_expiration: '2020-11-01T23:59:59Z',
151
+ x_4me_signature: 'foobar'
152
+ }
153
+ }
154
+ stub_request(:get, 'https://api.4me.com/v1/attachments/storage').with(credentials(authentication)).to_return(status: 200, body: resp.to_json)
160
155
 
161
- it 'should log an exception when the file could not be found' do
162
- expect_log('Attachment upload failed: file does not exist: unknown_file', :error)
163
- expect(attachments(authentication).send(:upload_attachment, nil, 'unknown_file', false)).to be_nil
164
- end
156
+ expect(a).to(receive(:upload_attachment).with('/tmp/file3.log').ordered { { key: 'attachments/5/requests/000/000/777/abc/file3.log', filesize: 5678 } })
157
+ expect(a).to(receive(:upload_attachment).with('/tmp/file1.png').ordered { { key: 'attachments/5/requests/000/000/777/abc/file1.png', filesize: 1234 } })
158
+ expect(a).to(receive(:upload_attachment).with('/tmp/file2.jpg').ordered { { key: 'attachments/5/requests/000/000/777/abc/file2.jpg', filesize: 9876 } })
165
159
 
166
- it 'should raise an exception when the file could not be found' do
167
- message = 'Attachment upload failed: file does not exist: unknown_file'
168
- expect{ attachments(authentication).send(:upload_attachment, nil, 'unknown_file', true) }.to raise_error(::Sdk4me::UploadFailed, message)
169
- end
170
-
171
- context 'aws' do
172
- before(:each) do
173
- @aws_conf = {
174
- provider: 'aws',
175
- upload_uri: 'https://itrp.s3.amazonaws.com/',
176
- access_key: 'AKIA6RYQ',
177
- success_url: 'https://mycompany.4me.com/s3_success?sig=99e82e8a046',
178
- policy: 'eydlgIH0=',
179
- signature: 'nbhdec4k=',
180
- upload_path: 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/'
160
+ data = {
161
+ subject: 'Foobar',
162
+ note: 'Foo [note_attachments: 2] Bar [note_attachments: 1]',
163
+ note_attachments: ['/tmp/file3.log', '/tmp/file1.png', '/tmp/file2.jpg']
181
164
  }
182
- @key_template = 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/${filename}'
183
- @key = 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/upload.txt'
184
-
185
- @multi_part_body = "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"Content-Type\"\r\n\r\napplication/octet-stream\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x-amz-server-side-encryption\"\r\n\r\nAES256\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"key\"\r\n\r\nattachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/${filename}\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"AWSAccessKeyId\"\r\n\r\nAKIA6RYQ\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"acl\"\r\n\r\nprivate\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"signature\"\r\n\r\nnbhdec4k=\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"policy\"\r\n\r\neydlgIH0=\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{@fixture_dir}/upload.txt\"\r\nContent-Type: text/plain\r\n\r\ncontent\r\n--0123456789ABLEWASIEREISAWELBA9876543210--"
186
- @multi_part_headers = {'Accept'=>'*/*', 'Content-Type'=>'multipart/form-data; boundary=0123456789ABLEWASIEREISAWELBA9876543210', 'User-Agent'=>'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6'}
165
+ a.upload_attachments!(data)
166
+
167
+ expect(data).to eq({
168
+ note: 'Foo ![](attachments/5/requests/000/000/777/abc/file2.jpg) Bar ![](attachments/5/requests/000/000/777/abc/file1.png)',
169
+ note_attachments: [
170
+ { filesize: 5678, key: 'attachments/5/requests/000/000/777/abc/file3.log' },
171
+ { filesize: 1234, inline: true, key: 'attachments/5/requests/000/000/777/abc/file1.png' },
172
+ { filesize: 9876, inline: true, key: 'attachments/5/requests/000/000/777/abc/file2.jpg' }
173
+ ],
174
+ subject: 'Foobar'
175
+ })
187
176
  end
188
177
 
189
- it 'should open a file from disk' do
190
- expect(attachments(authentication)).to receive(:aws_upload).with(@aws_conf, @key_template, @key, kind_of(File))
191
- expect(attachments(authentication).send(:upload_attachment, @aws_conf, "#{@fixture_dir}/upload.txt", false)).to eq({key: @key, filesize: 7})
192
- end
178
+ it 'failed uploads' do
179
+ a = attachments(authentication, '/requests')
180
+ resp = {
181
+ provider: 'local',
182
+ upload_uri: 'https://widget.example.com/attachments',
183
+ local: {
184
+ key: 'attachments/5/requests/000/000/777/abc/${filename}',
185
+ x_4me_expiration: '2020-11-01T23:59:59Z',
186
+ x_4me_signature: 'foobar'
187
+ }
188
+ }
189
+ stub_request(:get, 'https://api.4me.com/v1/attachments/storage').with(credentials(authentication)).to_return(status: 200, body: resp.to_json)
193
190
 
194
- it 'should sent the upload to AWS' do
195
- stub_request(:post, 'https://itrp.s3.amazonaws.com/').with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: 'OK', status: 303, headers: {'Location' => 'https://mycompany.4me.com/s3_success?sig=99e82e8a046'})
196
- stub_request(:get, "https://api.4me.com/v1/s3_success?sig=99e82e8a046&key=#{@key}").with(credentials(authentication)).to_return(body: {}.to_json)
197
- expect(attachments(authentication).send(:upload_attachment, @aws_conf, "#{@fixture_dir}/upload.txt", false)).to eq({key: @key, filesize: 7})
198
- end
191
+ expect_log('Attachment upload failed: file does not exist: /tmp/doesnotexist.png', :error)
199
192
 
200
- it 'should report an error when AWS upload fails' do
201
- stub_request(:post, 'https://itrp.s3.amazonaws.com/').with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: %(<?xml version="1.0" encoding="UTF-8"?>\n<Error><Code>AccessDenied</Code><Message>Invalid according to Policy</Message><RequestId>1FECC4B719E426B1</RequestId><HostId>15+14lXt+HlF</HostId></Error>), status: 303, headers: {'Location' => 'https://mycompany.4me.com/s3_success?sig=99e82e8a046'})
202
- expect_log("Attachment upload failed: AWS upload to https://itrp.s3.amazonaws.com/ for #{@key} failed: Invalid according to Policy", :error)
203
- expect(attachments(authentication).send(:upload_attachment, @aws_conf, "#{@fixture_dir}/upload.txt", false)).to be_nil
193
+ data = {
194
+ subject: 'Foobar',
195
+ note: 'Foo [note_attachments: 2] Bar [note_attachments: 1]',
196
+ note_attachments: ['/tmp/doesnotexist.png']
197
+ }
198
+ expect { a.upload_attachments!(data) }.to raise_error(::Sdk4me::UploadFailed, 'Attachment upload failed: file does not exist: /tmp/doesnotexist.png')
204
199
  end
200
+ end
201
+ end
205
202
 
206
- it 'should report an error when 4me confirmation fails' do
207
- stub_request(:post, 'https://itrp.s3.amazonaws.com/').with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: 'OK', status: 303, headers: {'Location' => 'https://mycompany.4me.com/s3_success?sig=99e82e8a046'})
208
- stub_request(:get, "https://api.4me.com/v1/s3_success?sig=99e82e8a046&key=#{@key}").with(credentials(authentication)).to_return(body: {message: 'oops!'}.to_json)
209
- expect_log('GET request to api.4me.com:443/v1/s3_success?sig=99e82e8a046&key=attachments%2F5%2Freqs%2F000%2F070%2F451%2Fzxxb4ot60xfd6sjg%2Fupload%2Etxt failed: oops!', :error)
210
- expect_log("Attachment upload failed: 4me confirmation s3_success?sig=99e82e8a046 for #{@key} failed: oops!", :error)
211
- expect(attachments(authentication).send(:upload_attachment, @aws_conf, "#{@fixture_dir}/upload.txt", false)).to be_nil
212
- end
203
+ context :upload_attachment do
204
+ before(:each) do
205
+ resp = {
206
+ provider: 'local',
207
+ upload_uri: 'https://widget.example.com/attachments',
208
+ local: {
209
+ key: 'attachments/5/requests/000/000/777/abc/${filename}',
210
+ x_4me_expiration: '2020-11-01T23:59:59Z',
211
+ x_4me_signature: 'foobar'
212
+ }
213
+ }
214
+ stub_request(:get, 'https://api.4me.com/v1/attachments/storage').with(credentials(authentication)).to_return(status: 200, body: resp.to_json)
215
+ end
213
216
 
214
- it 'should raise an exception when AWS upload fails' do
215
- stub_request(:post, 'https://itrp.s3.amazonaws.com/').with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: %(<?xml version="1.0" encoding="UTF-8"?>\n<Error><Code>AccessDenied</Code><Message>Invalid according to Policy</Message><RequestId>1FECC4B719E426B1</RequestId><HostId>15+14lXt+HlF</HostId></Error>), status: 303, headers: {'Location' => 'https://mycompany.4me.com/s3_success?sig=99e82e8a046'})
216
- message = "Attachment upload failed: AWS upload to https://itrp.s3.amazonaws.com/ for #{@key} failed: Invalid according to Policy"
217
- expect{ attachments(authentication).send(:upload_attachment, @aws_conf, "#{@fixture_dir}/upload.txt", true) }.to raise_error(::Sdk4me::UploadFailed, message)
218
- end
217
+ it 'should raise an error when the file could not be found' do
218
+ a = attachments(authentication, '/requests')
219
+ expect(@client).not_to receive(:send_file)
220
+ message = 'Attachment upload failed: file does not exist: /tmp/unknown_file'
221
+ expect_log(message, :error)
222
+ expect { a.send(:upload_attachment, '/tmp/unknown_file') }.to raise_error(::Sdk4me::UploadFailed, message)
219
223
  end
224
+ end
220
225
 
221
- context '4me' do
222
- before(:each) do
223
- @sdk4me_conf = {
224
- provider: 'local',
225
- upload_uri: 'https://api.4me.com/attachments',
226
- upload_path: 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/'
226
+ context :s3 do
227
+ before(:each) do
228
+ resp = {
229
+ provider: 's3',
230
+ upload_uri: 'https://example.s3-accelerate.amazonaws.com/',
231
+ s3: {
232
+ acl: 'private',
233
+ key: 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/${filename}',
234
+ policy: 'eydlgIH0=',
235
+ success_action_status: 201,
236
+ x_amz_algorithm: 'AWS4-HMAC-SHA256',
237
+ x_amz_credential: 'AKIATRO999Z9E9D2EQ7B/20201107/us-east-1/s3/aws4_request',
238
+ x_amz_date: '20201107T000000Z',
239
+ x_amz_server_side_encryption: 'AES256',
240
+ x_amz_signature: 'nbhdec4k='
227
241
  }
228
- @key_template = 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/${filename}'
229
- @key = 'attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/upload.txt'
230
-
231
- @multi_part_body = "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"Content-Type\"\r\n\r\napplication/octet-stream\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{@spec_dir}/support/fixtures/upload.txt\"\r\nContent-Type: text/plain\r\n\r\ncontent\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"key\"\r\n\r\nattachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/${filename}\r\n--0123456789ABLEWASIEREISAWELBA9876543210--"
232
- @multi_part_headers = {'Accept'=>'*/*', 'Content-Type'=>'multipart/form-data; boundary=0123456789ABLEWASIEREISAWELBA9876543210', 'User-Agent'=>'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6'}
233
- end
242
+ }
243
+ stub_request(:get, 'https://api.4me.com/v1/attachments/storage').with(credentials(authentication)).to_return(status: 200, body: resp.to_json)
244
+ end
234
245
 
235
- it 'should open a file from disk' do
236
- expect(attachments(authentication)).to receive(:upload_to_4me).with(@sdk4me_conf, @key_template, @key, kind_of(File))
237
- expect(attachments(authentication).send(:upload_attachment, @sdk4me_conf, "#{@fixture_dir}/upload.txt", false)).to eq({key: @key, filesize: 7})
246
+ it 'should upload a file from disk' do
247
+ Tempfile.create('4me_attachments_spec.txt') do |file|
248
+ file << 'foobar'
249
+ file.flush
250
+
251
+ a = attachments(authentication, '/requests')
252
+
253
+ stub_request(:post, 'https://example.s3-accelerate.amazonaws.com/')
254
+ .with(
255
+ body: "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"Content-Type\"\r\n\r\napplication/octet-stream\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"acl\"\r\n\r\nprivate\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"key\"\r\n\r\nattachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/${filename}\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"policy\"\r\n\r\neydlgIH0=\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_amz_algorithm\"\r\n\r\nAWS4-HMAC-SHA256\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_amz_credential\"\r\n\r\nAKIATRO999Z9E9D2EQ7B/20201107/us-east-1/s3/aws4_request\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_amz_date\"\r\n\r\n20201107T000000Z\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_amz_server_side_encryption\"\r\n\r\nAES256\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_amz_signature\"\r\n\r\nnbhdec4k=\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{file.path}\"\r\nContent-Type: application/octet-stream\r\n\r\nfoobar\r\n--0123456789ABLEWASIEREISAWELBA9876543210--",
256
+ headers: {
257
+ 'Accept' => '*/*',
258
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
259
+ 'Content-Type' => 'multipart/form-data; boundary=0123456789ABLEWASIEREISAWELBA9876543210',
260
+ 'User-Agent' => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6 4me/#{Sdk4me::Client::VERSION}"
261
+ }
262
+ )
263
+ .to_return(status: 200, body: '', headers: {})
264
+
265
+ expect(a.send(:upload_attachment, file.path)).to eq({
266
+ key: "attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/#{File.basename(file.path)}",
267
+ filesize: 6
268
+ })
238
269
  end
270
+ end
239
271
 
240
- it 'should sent the upload to 4me' do
241
- stub_request(:post, 'https://api.4me.com/v1/attachments').with(credentials(authentication)).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {}.to_json)
242
- expect(attachments(authentication).send(:upload_attachment, @sdk4me_conf, "#{@fixture_dir}/upload.txt", false)).to eq({key: @key, filesize: 7})
272
+ it 'should report an error when upload fails' do
273
+ Tempfile.create('4me_attachments_spec.txt') do |file|
274
+ file << 'foobar'
275
+ file.flush
276
+
277
+ a = attachments(authentication, '/requests')
278
+
279
+ stub_request(:post, 'https://example.s3-accelerate.amazonaws.com/')
280
+ .with(
281
+ body: "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"Content-Type\"\r\n\r\napplication/octet-stream\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"acl\"\r\n\r\nprivate\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"key\"\r\n\r\nattachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/${filename}\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"policy\"\r\n\r\neydlgIH0=\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_amz_algorithm\"\r\n\r\nAWS4-HMAC-SHA256\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_amz_credential\"\r\n\r\nAKIATRO999Z9E9D2EQ7B/20201107/us-east-1/s3/aws4_request\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_amz_date\"\r\n\r\n20201107T000000Z\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_amz_server_side_encryption\"\r\n\r\nAES256\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_amz_signature\"\r\n\r\nnbhdec4k=\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{file.path}\"\r\nContent-Type: application/octet-stream\r\n\r\nfoobar\r\n--0123456789ABLEWASIEREISAWELBA9876543210--",
282
+ headers: {
283
+ 'Accept' => '*/*',
284
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
285
+ 'Content-Type' => 'multipart/form-data; boundary=0123456789ABLEWASIEREISAWELBA9876543210',
286
+ 'User-Agent' => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6 4me/#{Sdk4me::Client::VERSION}"
287
+ }
288
+ )
289
+ .to_return(status: 400, body: '<Error><Message>Foo Bar Failure</Message></Error>', headers: {})
290
+
291
+ key = "attachments/5/reqs/000/070/451/zxxb4ot60xfd6sjg/#{File.basename(file.path)}"
292
+ message = "Attachment upload failed: AWS S3 upload to https://example.s3-accelerate.amazonaws.com/ for #{key} failed: Foo Bar Failure"
293
+ # expect_log(message, :error)
294
+ expect { a.send(:upload_attachment, file.path) }.to raise_error(::Sdk4me::UploadFailed, message)
243
295
  end
296
+ end
297
+ end
244
298
 
245
- it 'should report an error when 4me upload fails' do
246
- stub_request(:post, 'https://api.4me.com/v1/attachments').with(credentials(authentication)).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {message: 'oops!'}.to_json)
247
- expect_log('POST request to api.4me.com:443/v1/attachments failed: oops!', :error)
248
- expect_log("Attachment upload failed: 4me upload to https://api.4me.com/attachments for #{@key} failed: oops!", :error)
249
- expect(attachments(authentication).send(:upload_attachment, @sdk4me_conf, "#{@fixture_dir}/upload.txt", false)).to be_nil
250
- end
299
+ context '4me local' do
300
+ before(:each) do
301
+ resp = {
302
+ provider: 'local',
303
+ upload_uri: 'https://widget.example.com/attachments',
304
+ local: {
305
+ key: 'attachments/5/requests/000/000/777/abc/${filename}',
306
+ x_4me_expiration: '2020-11-01T23:59:59Z',
307
+ x_4me_signature: 'foobar'
308
+ }
309
+ }
310
+ stub_request(:get, 'https://api.4me.com/v1/attachments/storage').with(credentials(authentication)).to_return(status: 200, body: resp.to_json)
311
+ end
251
312
 
252
- it 'should raise an exception when 4me upload fails' do
253
- stub_request(:post, 'https://api.4me.com/v1/attachments').with(credentials(authentication)).with(body: @multi_part_body, headers: @multi_part_headers).to_return(body: {message: 'oops!'}.to_json)
254
- expect_log('POST request to api.4me.com:443/v1/attachments failed: oops!', :error)
255
- message = "Attachment upload failed: 4me upload to https://api.4me.com/attachments for #{@key} failed: oops!"
256
- expect{ attachments(authentication).send(:upload_attachment, @sdk4me_conf, "#{@fixture_dir}/upload.txt", true) }.to raise_error(::Sdk4me::UploadFailed, message)
313
+ it 'should upload a file from disk' do
314
+ Tempfile.create('4me_attachments_spec.txt') do |file|
315
+ file << 'foobar'
316
+ file.flush
317
+
318
+ a = attachments(authentication, '/requests')
319
+
320
+ stub_request(:post, 'https://widget.example.com/attachments')
321
+ .with(
322
+ body: "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"Content-Type\"\r\n\r\napplication/octet-stream\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"key\"\r\n\r\nattachments/5/requests/000/000/777/abc/${filename}\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_4me_expiration\"\r\n\r\n2020-11-01T23:59:59Z\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"x_4me_signature\"\r\n\r\nfoobar\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{file.path}\"\r\nContent-Type: application/octet-stream\r\n\r\nfoobar\r\n--0123456789ABLEWASIEREISAWELBA9876543210--",
323
+ headers: {
324
+ 'Accept' => '*/*',
325
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
326
+ 'Authorization' => (authentication == :api_token ? 'Basic c2VjcmV0Ong=' : 'Bearer secret'),
327
+ 'Content-Type' => 'multipart/form-data; boundary=0123456789ABLEWASIEREISAWELBA9876543210',
328
+ 'User-Agent' => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6 4me/#{Sdk4me::Client::VERSION}"
329
+ }
330
+ )
331
+ .to_return(status: 204, body: '', headers: {})
332
+
333
+ expect(a.send(:upload_attachment, file.path)).to eq({
334
+ key: "attachments/5/requests/000/000/777/abc/#{File.basename(file.path)}",
335
+ filesize: 6
336
+ })
257
337
  end
258
338
  end
259
-
260
339
  end
261
340
  end
262
341
  end