4me-sdk 1.1.7 → 1.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -4
- data/README.md +8 -0
- data/lib/sdk4me/client.rb +2 -1
- data/lib/sdk4me/client/attachments.rb +52 -9
- data/lib/sdk4me/client/version.rb +1 -1
- data/spec/lib/sdk4me/attachments_spec.rb +117 -51
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aba8de45bcc385f1f8ffedfce680f58cb5c73865510798712d7d8c382ecbd9bf
|
4
|
+
data.tar.gz: 1ffb444710f94ad590aa4943589465e028ad7cf1957bd5546fd93fe158871c6e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdd2c6e55c547a2b13ca893a73e3167d77adee143e53e396b48d8712cfb8d7e148f0f3b10090c3b20691e22b20ddad4a1593d6f8e374880a98c9f8b71abfbe85
|
7
|
+
data.tar.gz: d63bc178ea462a8b4691ad2d63c25ca48ae5735820d398047dfe98ff450be1a6d3f6c8aa8e27c30829afb9753b5213a6f55c0539d1ba03f134908ea8a59ae4f3
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
4me-sdk (1.1.
|
4
|
+
4me-sdk (1.1.8)
|
5
5
|
activesupport (>= 4.2)
|
6
6
|
gem_config (>= 0.3)
|
7
7
|
mime-types (>= 3.0)
|
@@ -16,7 +16,7 @@ GEM
|
|
16
16
|
tzinfo (~> 1.1)
|
17
17
|
addressable (2.5.2)
|
18
18
|
public_suffix (>= 2.0.2, < 4.0)
|
19
|
-
concurrent-ruby (1.1.
|
19
|
+
concurrent-ruby (1.1.5)
|
20
20
|
crack (0.4.3)
|
21
21
|
safe_yaml (~> 1.0.0)
|
22
22
|
diff-lcs (1.3)
|
@@ -28,7 +28,7 @@ GEM
|
|
28
28
|
json (2.1.0)
|
29
29
|
mime-types (3.2.2)
|
30
30
|
mime-types-data (~> 3.2015)
|
31
|
-
mime-types-data (3.
|
31
|
+
mime-types-data (3.2019.0331)
|
32
32
|
minitest (5.11.3)
|
33
33
|
public_suffix (3.0.3)
|
34
34
|
rake (12.3.1)
|
@@ -71,4 +71,4 @@ DEPENDENCIES
|
|
71
71
|
webmock (~> 2)
|
72
72
|
|
73
73
|
BUNDLED WITH
|
74
|
-
1.
|
74
|
+
1.17.3
|
data/README.md
CHANGED
@@ -213,6 +213,14 @@ response = Sdk4me::Client.new.put('requests/416621', {
|
|
213
213
|
})
|
214
214
|
```
|
215
215
|
|
216
|
+
It is also possible to add inline attachments as follows:
|
217
|
+
```
|
218
|
+
response = Sdk4me::Client.new.put('requests/416621', {
|
219
|
+
note: 'Here is some inspiration for you: [attachment:/tmp/images/puppy.png]'
|
220
|
+
})
|
221
|
+
```
|
222
|
+
Note that only images are accepted as inline attachments.
|
223
|
+
|
216
224
|
If an attachment upload fails, the errors are logged but the `post` or `put` request will still be sent to 4me without the
|
217
225
|
failed attachments. To receive exceptions add `attachments_exception: true` to the data.
|
218
226
|
|
data/lib/sdk4me/client.rb
CHANGED
@@ -211,7 +211,8 @@ module Sdk4me
|
|
211
211
|
def expand_header(header = {})
|
212
212
|
header = DEFAULT_HEADER.merge(header)
|
213
213
|
header['X-4me-Account'] = option(:account) if option(:account)
|
214
|
-
|
214
|
+
token_and_password = option(:api_token).include?(':') ? option(:api_token) : "#{option(:api_token)}:x"
|
215
|
+
header['AUTHORIZATION'] = 'Basic ' + [token_and_password].pack('m*').gsub(/\s/, '')
|
215
216
|
if option(:source)
|
216
217
|
header['X-4me-Source'] = option(:source)
|
217
218
|
header['HTTP_USER_AGENT'] = option(:source)
|
@@ -8,21 +8,64 @@ module Sdk4me
|
|
8
8
|
@client = client
|
9
9
|
end
|
10
10
|
|
11
|
-
# upload the attachments
|
11
|
+
# upload the attachments and return the data with the uploaded attachment info
|
12
|
+
# Two flavours available
|
13
|
+
# * data[:attachments]
|
14
|
+
# * data[:note] containing text with '[attachment:/tmp/images/green_fuzz.jpg]'
|
12
15
|
def upload_attachments!(path, data)
|
13
|
-
|
16
|
+
upload_options = {
|
17
|
+
raise_exceptions: !!data.delete(:attachments_exception),
|
18
|
+
attachments_field: attachments_field(path),
|
19
|
+
}
|
20
|
+
uploaded_attachments = upload_normal_attachments!(path, data, upload_options)
|
21
|
+
uploaded_attachments += upload_inline_attachments!(path, data, upload_options)
|
22
|
+
# jsonify the attachments, if any were uploaded
|
23
|
+
data[upload_options[:attachments_field]] = uploaded_attachments.compact.to_json if uploaded_attachments.compact.any?
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# upload the attachments in :attachments to 4me and return the data with the uploaded attachment info
|
29
|
+
def upload_normal_attachments!(path, data, upload_options)
|
14
30
|
attachments = [data.delete(:attachments)].flatten.compact
|
15
|
-
return if attachments.empty?
|
31
|
+
return [] if attachments.empty?
|
16
32
|
|
17
|
-
|
18
|
-
|
19
|
-
report_error("Attachments not allowed for #{path}", raise_exceptions) and return unless storage
|
33
|
+
upload_options[:storage] ||= storage(path, upload_options[:raise_exceptions])
|
34
|
+
return [] unless upload_options[:storage]
|
20
35
|
|
21
|
-
|
22
|
-
|
36
|
+
attachments.map do |attachment|
|
37
|
+
upload_attachment(upload_options[:storage], attachment, upload_options[:raise_exceptions])
|
38
|
+
end
|
23
39
|
end
|
24
40
|
|
25
|
-
|
41
|
+
INLINE_ATTACHMENT_REGEXP = /\[attachment:([^\]]+)\]/.freeze
|
42
|
+
# upload any '[attachment:/tmp/images/green_fuzz.jpg]' in :note text field to 4me as inline attachment and add the s3 key to the text
|
43
|
+
def upload_inline_attachments!(path, data, upload_options)
|
44
|
+
text_field = upload_options[:attachments_field].to_s.gsub('_attachments', '').to_sym
|
45
|
+
return [] unless (data[text_field] || '') =~ INLINE_ATTACHMENT_REGEXP
|
46
|
+
|
47
|
+
upload_options[:storage] ||= storage(path, upload_options[:raise_exceptions])
|
48
|
+
return [] unless upload_options[:storage]
|
49
|
+
|
50
|
+
attachments = []
|
51
|
+
data[text_field] = data[text_field].gsub(INLINE_ATTACHMENT_REGEXP) do |full_match|
|
52
|
+
attachment_details = upload_attachment(upload_options[:storage], $~[1], upload_options[:raise_exceptions])
|
53
|
+
if attachment_details
|
54
|
+
attachments << attachment_details.merge(inline: true)
|
55
|
+
"" # magic markdown for inline attachments
|
56
|
+
else
|
57
|
+
full_match
|
58
|
+
end
|
59
|
+
end
|
60
|
+
attachments
|
61
|
+
end
|
62
|
+
|
63
|
+
def storage(path, raise_exceptions)
|
64
|
+
# retrieve the upload configuration for this record from 4me
|
65
|
+
storage = @client.get(path =~ /\d+$/ ? path : "#{path}/new", {attachment_upload_token: true}, @client.send(:expand_header))[:storage_upload]
|
66
|
+
report_error("Attachments not allowed for #{path}", raise_exceptions) unless storage
|
67
|
+
storage
|
68
|
+
end
|
26
69
|
|
27
70
|
def attachments_field(path)
|
28
71
|
case path
|
@@ -8,69 +8,135 @@ describe Sdk4me::Attachments do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
context 'upload_attachments!' do
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
context 'normal' do
|
12
|
+
it 'should not do anything when no :attachments are present' do
|
13
|
+
expect(@attachments.upload_attachments!('/requests', {status: :in_progress})).to be_nil
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
it 'should not do anything when :attachments is nil' do
|
17
|
+
expect(@attachments.upload_attachments!('/requests', {attachments: nil})).to be_nil
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
it 'should not do anything when :attachments is empty' do
|
21
|
+
expect(@attachments.upload_attachments!('/requests', {attachments: []})).to be_nil
|
22
|
+
expect(@attachments.upload_attachments!('/requests', {attachments: [nil]})).to be_nil
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
it 'should show a error if no attachment may be uploaded' do
|
26
|
+
stub_request(:get, 'https://api.4me.com/v1/sites/1?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {name: 'site 1'}.to_json)
|
27
|
+
expect_log('Attachments not allowed for /sites/1', :error)
|
28
|
+
expect(@attachments.upload_attachments!('/sites/1', {attachments: ['file1.png']})).to be_nil
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
it 'should raise an exception if no attachment may be uploaded' do
|
32
|
+
stub_request(:get, 'https://api.4me.com/v1/sites/1?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {name: 'site 1'}.to_json)
|
33
|
+
message = 'Attachments not allowed for /sites/1'
|
34
|
+
expect{ @attachments.upload_attachments!('/sites/1', {attachments: ['file1.png'], attachments_exception: true}) }.to raise_error(::Sdk4me::UploadFailed, message)
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
it 'should add /new to the path for new records' do
|
38
|
+
stub_request(:get, 'https://api.4me.com/v1/sites/new?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {missing: 'storage'}.to_json)
|
39
|
+
expect_log('Attachments not allowed for /sites', :error)
|
40
|
+
expect(@attachments.upload_attachments!('/sites', {attachments: ['file1.png']})).to be_nil
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
43
|
+
[ [:requests, :note],
|
44
|
+
[:problems, :note],
|
45
|
+
[:contracts, :remarks],
|
46
|
+
[:cis, :remarks],
|
47
|
+
[:flsas, :remarks],
|
48
|
+
[:slas, :remarks],
|
49
|
+
[:service_instances, :remarks],
|
50
|
+
[:service_offerings, :summary],
|
51
|
+
[:any_other_model, :note]].each do |model, attribute|
|
52
|
+
|
53
|
+
it "should replace :attachments with :#{attribute}_attachments after upload at /#{model}" do
|
54
|
+
stub_request(:get, "https://api.4me.com/v1/#{model}/new?attachment_upload_token=true").with(basic_auth: ['secret', 'x']).to_return(body: {storage_upload: 'conf'}.to_json)
|
55
|
+
expect(@attachments).to receive(:upload_attachment).with('conf', 'file1.png', false).ordered{ 'uploaded file1.png' }
|
56
|
+
expect(@attachments).to receive(:upload_attachment).with('conf', 'file2.zip', false).ordered{ 'uploaded file2.zip' }
|
57
|
+
data = {leave: 'me alone', attachments: %w(file1.png file2.zip)}
|
58
|
+
@attachments.upload_attachments!("/#{model}", data)
|
59
|
+
expect(data[:attachments]).to be_nil
|
60
|
+
expect(data[:leave]).to eq('me alone')
|
61
|
+
expect(data[:"#{attribute}_attachments"]).to eq(['uploaded file1.png', 'uploaded file2.zip'].to_json)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should set raise_exception flag to true when :attachments_exception is set' do
|
66
|
+
stub_request(:get, 'https://api.4me.com/v1/requests/new?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {storage_upload: 'conf'}.to_json)
|
67
|
+
expect(@attachments).to receive(:upload_attachment).with('conf', 'file1.png', true).ordered{ 'uploaded file1.png' }
|
68
|
+
data = {leave: 'me alone', attachments: 'file1.png', attachments_exception: true}
|
69
|
+
@attachments.upload_attachments!('/requests', data)
|
58
70
|
expect(data[:attachments]).to be_nil
|
71
|
+
expect(data[:attachments_exception]).to be_nil
|
59
72
|
expect(data[:leave]).to eq('me alone')
|
60
|
-
expect(data[:
|
73
|
+
expect(data[:note_attachments]).to eq(['uploaded file1.png'].to_json)
|
61
74
|
end
|
62
75
|
end
|
63
76
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
77
|
+
context 'inline' do
|
78
|
+
it 'should not do anything when no [attachment:...] is present in the note' do
|
79
|
+
expect(@attachments.upload_attachments!('/requests', {note: '[attachmen:/type]'})).to be_nil
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should not do anything when attachment is empty' do
|
83
|
+
expect(@attachments.upload_attachments!('/requests', {note: '[attachment:]'})).to be_nil
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should show a error if no attachment may be uploaded' do
|
87
|
+
stub_request(:get, 'https://api.4me.com/v1/sites/1?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {name: 'site 1'}.to_json)
|
88
|
+
expect_log('Attachments not allowed for /sites/1', :error)
|
89
|
+
expect(@attachments.upload_attachments!('/sites/1', {note: '[attachment:file1.png]'})).to be_nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should raise an exception if no attachment may be uploaded' do
|
93
|
+
stub_request(:get, 'https://api.4me.com/v1/sites/1?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {name: 'site 1'}.to_json)
|
94
|
+
message = 'Attachments not allowed for /sites/1'
|
95
|
+
expect{ @attachments.upload_attachments!('/sites/1', {note: '[attachment:file1.png]', attachments_exception: true}) }.to raise_error(::Sdk4me::UploadFailed, message)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should add /new to the path for new records' do
|
99
|
+
stub_request(:get, 'https://api.4me.com/v1/sites/new?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {missing: 'storage'}.to_json)
|
100
|
+
expect_log('Attachments not allowed for /sites', :error)
|
101
|
+
expect(@attachments.upload_attachments!('/sites', {note: '[attachment:file1.png]'})).to be_nil
|
102
|
+
end
|
103
|
+
|
104
|
+
[ [:requests, :note],
|
105
|
+
[:problems, :note],
|
106
|
+
[:contracts, :remarks],
|
107
|
+
[:cis, :remarks],
|
108
|
+
[:flsas, :remarks],
|
109
|
+
[:slas, :remarks],
|
110
|
+
[:service_instances, :remarks],
|
111
|
+
[:service_offerings, :summary],
|
112
|
+
[:any_other_model, :note]].each do |model, attribute|
|
113
|
+
|
114
|
+
it "should replace :attachments with :#{attribute}_attachments after upload at /#{model}" do
|
115
|
+
stub_request(:get, "https://api.4me.com/v1/#{model}/new?attachment_upload_token=true").with(basic_auth: ['secret', 'x']).to_return(body: {storage_upload: 'conf'}.to_json)
|
116
|
+
expect(@attachments).to receive(:upload_attachment).with('conf', 'file1.png', false).ordered{ {key: 'uploaded file1.png'} }
|
117
|
+
expect(@attachments).to receive(:upload_attachment).with('conf', 'file2.zip', false).ordered{ {key: 'uploaded file2.zip'} }
|
118
|
+
data = {leave: 'me alone', attribute => '[attachment:file1.png] and [attachment:file2.zip]'}
|
119
|
+
@attachments.upload_attachments!("/#{model}", data)
|
120
|
+
expect(data[:attachments]).to be_nil
|
121
|
+
expect(data[:leave]).to eq('me alone')
|
122
|
+
expect(data[:"#{attribute}_attachments"]).to eq([{key: 'uploaded file1.png', inline: true}, {key: 'uploaded file2.zip', inline: true}].to_json)
|
123
|
+
expect(data[:"#{attribute}"]).to eq(' and ')
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should set raise_exception flag to true when :attachments_exception is set' do
|
128
|
+
stub_request(:get, 'https://api.4me.com/v1/requests/new?attachment_upload_token=true').with(basic_auth: ['secret', 'x']).to_return(body: {storage_upload: 'conf'}.to_json)
|
129
|
+
expect(@attachments).to receive(:upload_attachment).with('conf', 'file1.png', true).ordered{ {key: 'uploaded file1.png'} }
|
130
|
+
data = {leave: 'me alone', note: '[attachment:file1.png]', attachments_exception: true}
|
131
|
+
@attachments.upload_attachments!('/requests', data)
|
132
|
+
expect(data[:attachments]).to be_nil
|
133
|
+
expect(data[:attachments_exception]).to be_nil
|
134
|
+
expect(data[:leave]).to eq('me alone')
|
135
|
+
expect(data[:note_attachments]).to eq([{key: 'uploaded file1.png', inline: true}].to_json)
|
136
|
+
expect(data[:note]).to eq('')
|
137
|
+
end
|
73
138
|
end
|
139
|
+
|
74
140
|
end
|
75
141
|
|
76
142
|
context 'upload_attachment' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: 4me-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- 4me
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-02-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gem_config
|