asana_exception_notifier 0.0.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.
- checksums.yaml +7 -0
- data/.codeclimate.yml +26 -0
- data/.reek +11 -0
- data/.rubocop.yml +76 -0
- data/.travis.yml +16 -0
- data/CONTRIBUTING.md +46 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +180 -0
- data/LICENSE +20 -0
- data/README.md +225 -0
- data/Rakefile +37 -0
- data/asana_exception_notifier.gemspec +48 -0
- data/examples/sinatra/Gemfile +7 -0
- data/examples/sinatra/Gemfile.lock +124 -0
- data/examples/sinatra/Procfile +1 -0
- data/examples/sinatra/README.md +14 -0
- data/examples/sinatra/config.ru +3 -0
- data/examples/sinatra/sinatra_app.rb +46 -0
- data/init.rb +1 -0
- data/lib/asana_exception_notifier/classes/asana.rb +95 -0
- data/lib/asana_exception_notifier/classes/error_page.rb +134 -0
- data/lib/asana_exception_notifier/helpers/application_helper.rb +286 -0
- data/lib/asana_exception_notifier/initializers/zip.rb +14 -0
- data/lib/asana_exception_notifier/note_templates/asana_exception_notifier.html.erb +82 -0
- data/lib/asana_exception_notifier/note_templates/asana_exception_notifier.text.erb +24 -0
- data/lib/asana_exception_notifier/request/client.rb +85 -0
- data/lib/asana_exception_notifier/request/core.rb +132 -0
- data/lib/asana_exception_notifier/request/middleware.rb +41 -0
- data/lib/asana_exception_notifier/version.rb +27 -0
- data/lib/asana_exception_notifier.rb +45 -0
- data/lib/generators/asana_exception_notifier/install_generator.rb +14 -0
- data/lib/generators/asana_exception_notifier/templates/asana_exception_notifier.rb +20 -0
- data/spec/lib/asana_exception_notifier/classes/asana_spec.rb +23 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/test_notification.rb +20 -0
- metadata +467 -0
@@ -0,0 +1,286 @@
|
|
1
|
+
module AsanaExceptionNotifier
|
2
|
+
# module that is used for formatting numbers using metrics
|
3
|
+
module ApplicationHelper
|
4
|
+
# function that makes the methods incapsulated as utility functions
|
5
|
+
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def permitted_options
|
9
|
+
{
|
10
|
+
asana_api_key: nil,
|
11
|
+
workspace: nil,
|
12
|
+
assignee: nil,
|
13
|
+
assignee_status: nil,
|
14
|
+
due_at: nil,
|
15
|
+
due_on: nil,
|
16
|
+
hearted: false,
|
17
|
+
hearts: [],
|
18
|
+
projects: [],
|
19
|
+
followers: [],
|
20
|
+
memberships: [],
|
21
|
+
tags: [],
|
22
|
+
notes: '',
|
23
|
+
name: '',
|
24
|
+
template_path: default_template_path
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def multi_request_manager
|
29
|
+
@multi_manager ||= EventMachine::MultiRequest.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def extract_body(env)
|
33
|
+
return if env.blank? || !env.is_a?(Hash)
|
34
|
+
io = env['rack.input']
|
35
|
+
io.rewind if io.respond_to?(:rewind)
|
36
|
+
io.read
|
37
|
+
end
|
38
|
+
|
39
|
+
def show_hash_content(hash)
|
40
|
+
hash.map do |key, value|
|
41
|
+
value.is_a?(Hash) ? show_hash_content(value) : ["#{key}:", value]
|
42
|
+
end.join("\n ")
|
43
|
+
end
|
44
|
+
|
45
|
+
def tempfile_details(tempfile)
|
46
|
+
file_details = get_extension_and_name_from_file(tempfile)
|
47
|
+
{
|
48
|
+
file: tempfile,
|
49
|
+
path: tempfile.path
|
50
|
+
}.merge(file_details)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns utf8 encoding of the msg
|
54
|
+
# @param [String] msg
|
55
|
+
# @return [String] ReturnsReturns utf8 encoding of the msg
|
56
|
+
def force_utf8_encoding(msg)
|
57
|
+
msg.respond_to?(:force_encoding) && msg.encoding.name != 'UTF-8' ? msg.force_encoding('UTF-8') : msg
|
58
|
+
end
|
59
|
+
|
60
|
+
# returns the logger used to log messages and errors
|
61
|
+
#
|
62
|
+
# @return [Logger]
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
def logger
|
66
|
+
@logger ||= defined?(Rails) ? Rails.logger : ExceptionNotifier.logger
|
67
|
+
end
|
68
|
+
|
69
|
+
def ensure_eventmachine_running(&block)
|
70
|
+
Thread.abort_on_exception = true
|
71
|
+
register_em_error_handler
|
72
|
+
run_em_reactor(&block)
|
73
|
+
end
|
74
|
+
|
75
|
+
def register_em_error_handler
|
76
|
+
EM.error_handler do |error|
|
77
|
+
logger.debug '[AsanaExceptionNotifier]: Error during event loop :'
|
78
|
+
logger.debug "[AsanaExceptionNotifier]: #{log_exception(error)}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def log_exception(exception)
|
83
|
+
logger.debug exception.inspect
|
84
|
+
log_bactrace(exception) if exception.respond_to?(:backtrace)
|
85
|
+
end
|
86
|
+
|
87
|
+
def log_bactrace(exception)
|
88
|
+
logger.debug exception.backtrace.join("\n")
|
89
|
+
end
|
90
|
+
|
91
|
+
def execute_with_rescue(options = {})
|
92
|
+
yield if block_given?
|
93
|
+
rescue Interrupt
|
94
|
+
rescue_interrupt
|
95
|
+
rescue => error
|
96
|
+
log_exception(error)
|
97
|
+
options.fetch(:value, '')
|
98
|
+
end
|
99
|
+
|
100
|
+
def rescue_interrupt
|
101
|
+
`stty icanon echo`
|
102
|
+
puts "\n Command was cancelled due to an Interrupt error."
|
103
|
+
end
|
104
|
+
|
105
|
+
def run_em_reactor
|
106
|
+
Thread.new do
|
107
|
+
EM.run do
|
108
|
+
EM.defer proc { yield if block_given? }
|
109
|
+
end
|
110
|
+
end.join
|
111
|
+
end
|
112
|
+
|
113
|
+
def template_dir
|
114
|
+
File.expand_path(File.join(root, 'note_templates'))
|
115
|
+
end
|
116
|
+
|
117
|
+
def default_template_path
|
118
|
+
File.join(template_dir, 'asana_exception_notifier.html.erb')
|
119
|
+
end
|
120
|
+
|
121
|
+
def template_path_exist(path)
|
122
|
+
return path if File.exist?(path)
|
123
|
+
fail ArgumentError, "file #{path} doesn't exist"
|
124
|
+
end
|
125
|
+
|
126
|
+
def max_length(rows, index)
|
127
|
+
value = rows.max_by { |array| array[index].to_s.size }
|
128
|
+
value.is_a?(Array) ? value[index] : value
|
129
|
+
end
|
130
|
+
|
131
|
+
def get_hash_rows(hash, rows = [], prefix = '')
|
132
|
+
hash.each do |key, value|
|
133
|
+
if value.is_a?(Hash)
|
134
|
+
get_hash_rows(value, rows, key)
|
135
|
+
else
|
136
|
+
rows.push(["#{prefix}#{key}".inspect, escape(value.inspect)])
|
137
|
+
end
|
138
|
+
end
|
139
|
+
rows
|
140
|
+
end
|
141
|
+
|
142
|
+
def link_helper(link)
|
143
|
+
<<-LINK
|
144
|
+
<a href="javascript:void(0)" onclick="AjaxExceptionNotifier.hideAllAndToggle('#{link.downcase}')">#{link.camelize}</a>
|
145
|
+
LINK
|
146
|
+
end
|
147
|
+
|
148
|
+
def escape(text)
|
149
|
+
text.gsub('&', '&').gsub('<', '<').gsub('>', '>')
|
150
|
+
end
|
151
|
+
|
152
|
+
def set_fieldset_key(links, prefix)
|
153
|
+
links[prefix] ||= {}
|
154
|
+
prefix
|
155
|
+
end
|
156
|
+
|
157
|
+
def parse_fieldset_value(options)
|
158
|
+
value = options[:value]
|
159
|
+
value.is_a?(Hash) ? value.reject! { |_new_key, new_value| new_value.is_a?(Hash) } : value
|
160
|
+
end
|
161
|
+
|
162
|
+
# Gets a bidimensional array and create a table.
|
163
|
+
# The first array is used as label.
|
164
|
+
#
|
165
|
+
def mount_table(array, options = {})
|
166
|
+
return '' if array.blank?
|
167
|
+
header = array.shift
|
168
|
+
|
169
|
+
header = header.map { |name| escape(name.to_s.humanize) }
|
170
|
+
rows = array.map { |name| "<tr><td>#{name.join('</td><td>')}</td></tr>" }
|
171
|
+
|
172
|
+
<<-TABLE
|
173
|
+
<table #{hash_to_html_attributes(options)}>
|
174
|
+
<thead><tr><th>#{header.join('</th><th>')}</th></tr></thead>
|
175
|
+
<tbody>#{rows.join}</tbody>
|
176
|
+
</table>
|
177
|
+
TABLE
|
178
|
+
end
|
179
|
+
|
180
|
+
# Mount table for hash, using name and value and adding a name_value class
|
181
|
+
# to the generated table.
|
182
|
+
#
|
183
|
+
def mount_table_for_hash(hash, options = {})
|
184
|
+
return if hash.blank?
|
185
|
+
rows = get_hash_rows(hash, options.fetch('rows', []))
|
186
|
+
mount_table(rows.unshift(%w(Name Value)), { class: 'name_values' }.merge(options))
|
187
|
+
end
|
188
|
+
|
189
|
+
def hash_to_html_attributes(hash)
|
190
|
+
hash.map do |key, value|
|
191
|
+
"#{key}=\"#{value.gsub('"', '\"')}\" "
|
192
|
+
end.join(' ')
|
193
|
+
end
|
194
|
+
|
195
|
+
# returns the root path of the gem
|
196
|
+
#
|
197
|
+
# @return [void]
|
198
|
+
#
|
199
|
+
# @api public
|
200
|
+
def root
|
201
|
+
File.expand_path(File.dirname(__dir__))
|
202
|
+
end
|
203
|
+
|
204
|
+
def get_extension_and_name_from_file(tempfile)
|
205
|
+
path = tempfile.respond_to?(:path) ? tempfile.path : tempfile
|
206
|
+
pathname = Pathname.new(path)
|
207
|
+
extension = pathname.extname
|
208
|
+
{
|
209
|
+
extension: extension,
|
210
|
+
filename: File.basename(pathname, extension),
|
211
|
+
file_path: path
|
212
|
+
}
|
213
|
+
end
|
214
|
+
|
215
|
+
def setup_em_options(options)
|
216
|
+
options.symbolize_keys!
|
217
|
+
options[:em_request] ||= {}
|
218
|
+
options
|
219
|
+
end
|
220
|
+
|
221
|
+
def create_upload_file_part(file)
|
222
|
+
Part.new(name: 'file',
|
223
|
+
body: force_utf8_encoding(File.read(file)),
|
224
|
+
filename: file,
|
225
|
+
content_type: 'application/zip'
|
226
|
+
)
|
227
|
+
end
|
228
|
+
|
229
|
+
def multipart_file_upload_details(file)
|
230
|
+
boundary = "---------------------------#{rand(10_000_000_000_000_000_000)}"
|
231
|
+
body = MultipartBody.new([create_upload_file_part(file)], boundary)
|
232
|
+
file_upload_request_options(boundary, body, file)
|
233
|
+
end
|
234
|
+
|
235
|
+
def file_upload_request_options(boundary, body, file)
|
236
|
+
{
|
237
|
+
body: body.to_s,
|
238
|
+
head:
|
239
|
+
{
|
240
|
+
'Content-Type' => "multipart/form-data;boundary=#{boundary}",
|
241
|
+
'Content-Length' => File.size(file),
|
242
|
+
'Expect' => '100-continue'
|
243
|
+
}
|
244
|
+
}
|
245
|
+
end
|
246
|
+
|
247
|
+
def get_response_from_request(http, _options)
|
248
|
+
http.respond_to?(:response) ? http.response : http.responses
|
249
|
+
end
|
250
|
+
|
251
|
+
def get_multi_request_values(http_response, key)
|
252
|
+
response_method = key.to_s == 'callback' ? 'response' : 'error'
|
253
|
+
http_response[key.to_sym].values.map { |request| request.send(response_method) }.reject(&:blank?)
|
254
|
+
end
|
255
|
+
|
256
|
+
def split_archive(archive, partial_name, segment_size)
|
257
|
+
indexes = Zip::File.split(archive, segment_size, true, partial_name)
|
258
|
+
archives = Array.new(indexes) do |index|
|
259
|
+
File.join(File.dirname(archive), "#{partial_name}.zip.#{format('%03d', index + 1)}")
|
260
|
+
end if indexes.present?
|
261
|
+
archives.blank? ? [archive] : archives
|
262
|
+
end
|
263
|
+
|
264
|
+
def compress_files(directory, name, files)
|
265
|
+
archive = create_archive(directory, name)
|
266
|
+
::Zip::File.open(archive, Zip::File::CREATE) do |zipfile|
|
267
|
+
add_files_to_zip(zipfile, files)
|
268
|
+
end
|
269
|
+
archive
|
270
|
+
end
|
271
|
+
|
272
|
+
def add_files_to_zip(zipfile, files)
|
273
|
+
files.each do |file|
|
274
|
+
zipfile.add(file.sub(File.dirname(file) + '/', ''), file)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def create_archive(directory, name)
|
279
|
+
archive = File.join(directory, name + '.zip')
|
280
|
+
archive_dir = File.dirname(archive)
|
281
|
+
FileUtils.mkdir_p(archive_dir) unless File.directory?(archive_dir)
|
282
|
+
FileUtils.rm archive, force: true if File.exist?(archive)
|
283
|
+
archive
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Zip.setup do |c|
|
2
|
+
c.on_exists_proc = true
|
3
|
+
c.continue_on_exists_proc = true
|
4
|
+
c.unicode_names = false
|
5
|
+
c.default_compression = Zlib::BEST_COMPRESSION
|
6
|
+
end
|
7
|
+
|
8
|
+
Zip::File.class_eval do
|
9
|
+
singleton_class.send(:alias_method, :original_get_segment_size_for_split, :get_segment_size_for_split)
|
10
|
+
|
11
|
+
def self.get_segment_size_for_split(segment_size)
|
12
|
+
segment_size
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<style type="text/css">
|
5
|
+
#asana_exception_notifier {font-size: 11px; font-family: Consolas, monaco, monospace; font-weight: normal; margin: 2em 0 1em 0; text-align: center; color: #444; line-height: 16px; background: #fff;}
|
6
|
+
#asana_exception_notifier th, #asana_exception_notifier td {color: #444; line-height: 18px;}
|
7
|
+
#asana_exception_notifier a {color: #9b1b1b; font-weight: inherit; text-decoration: none; line-height: 18px;}
|
8
|
+
#asana_exception_notifier table {text-align: left; width: 100%;}
|
9
|
+
#asana_exception_notifier table td {padding: 5px; border-bottom: 1px solid #ccc;}
|
10
|
+
#asana_exception_notifier table td strong {color: #9b1b1b;}
|
11
|
+
#asana_exception_notifier table th {padding: 5px; border-bottom: 1px solid #ccc;}
|
12
|
+
#asana_exception_notifier table tr:nth-child(2n) td {background: #eee;}
|
13
|
+
#asana_exception_notifier table tr:nth-child(2n + 1) td {background: #fff;}
|
14
|
+
#asana_exception_notifier tbody {text-align: left;}
|
15
|
+
#asana_exception_notifier .name_values td {vertical-align: top;}
|
16
|
+
#asana_exception_notifier legend {background-color: #fff;}
|
17
|
+
#asana_exception_notifier fieldset {text-align: left; border: 1px dashed #aaa; padding: 0.5em 1em 1em 1em; margin: 1em 2em; color: #444; background-color: #FFF;}
|
18
|
+
</style>
|
19
|
+
</head>
|
20
|
+
<body>
|
21
|
+
<div style="clear:both"></div>
|
22
|
+
<div id="asana_exception_notifier">
|
23
|
+
<div id="all">
|
24
|
+
Show: <%= fieldsets_links %>
|
25
|
+
<% fieldsets.each do |key, value| %>
|
26
|
+
<fieldset class='ajax_exception_notifier_fieldset' id='<%= key %>' style="<%= fieldsets.keys.first != key ? "display:none" : '' %>">
|
27
|
+
<legend>Debug Information for <%= key.to_s.camelize %></legend>
|
28
|
+
<div>
|
29
|
+
<%= mount_table_for_hash(value) %>
|
30
|
+
</div>
|
31
|
+
</fieldset>
|
32
|
+
<% end %>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
<script type="text/javascript">
|
36
|
+
var AjaxExceptionNotifier = function() {
|
37
|
+
function hideAll(){
|
38
|
+
fields = document.getElementsByClassName('ajax_exception_notifier_fieldset')
|
39
|
+
for (index = 0; index < fields.length; ++index) {
|
40
|
+
AjaxExceptionNotifier.hide(fields[index]);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
function hideAllAndToggle(id) {
|
44
|
+
var n = note(id);
|
45
|
+
var display = n.style.display;
|
46
|
+
hideAll();
|
47
|
+
// Restore original display to allow toggling
|
48
|
+
n.style.display = display;
|
49
|
+
toggle(id)
|
50
|
+
location.href = '#ajax_exception_notifier';
|
51
|
+
}
|
52
|
+
function note(id) {
|
53
|
+
console.log(id);
|
54
|
+
return (document.getElementById(id));
|
55
|
+
}
|
56
|
+
function toggle(id){
|
57
|
+
var el = note(id);
|
58
|
+
if (el.style.display == 'none') {
|
59
|
+
AjaxExceptionNotifier.show(el);
|
60
|
+
} else {
|
61
|
+
AjaxExceptionNotifier.hide(el);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
function show(element) {
|
65
|
+
element.style.display = 'block'
|
66
|
+
}
|
67
|
+
function hide(element) {
|
68
|
+
element.style.display = 'none'
|
69
|
+
}
|
70
|
+
return {
|
71
|
+
show: show,
|
72
|
+
hide: hide,
|
73
|
+
toggle: toggle,
|
74
|
+
hideAll: hideAll,
|
75
|
+
node: note,
|
76
|
+
hideAllAndToggle: hideAllAndToggle
|
77
|
+
}
|
78
|
+
}();
|
79
|
+
/* Additional Javascript */
|
80
|
+
</script>
|
81
|
+
</body>
|
82
|
+
</html>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
A <%= exception.class.to_s %> occured: <%= exception.message.to_s %>
|
2
|
+
<% if exception.respond_to?(:backtrace) && exception.backtrace.present? %>
|
3
|
+
File: <%= exception.backtrace.slice(0, 1).join("\n ") %>
|
4
|
+
<% end %>
|
5
|
+
<% if request_data[:ip_address].present? %>
|
6
|
+
IP: <%= request_data[:ip_address] %>
|
7
|
+
<% end %>
|
8
|
+
<% if request_data[:session].present? %>
|
9
|
+
Session: <%= request_data[:session]%>
|
10
|
+
<% end %>
|
11
|
+
<% if request_data[:url].present? %>
|
12
|
+
Request URI: <%= request_data[:url] %>
|
13
|
+
<% end %>
|
14
|
+
<% if request_data[:referrer].present? %>
|
15
|
+
Referer: <%= request_data[:referrer] %>
|
16
|
+
<% end %>
|
17
|
+
PID: <%= process %>
|
18
|
+
PWD: <%= pwd %>
|
19
|
+
Uname: <%= uname %>
|
20
|
+
Server: <%= server %>
|
21
|
+
Error time: <%= timestamp %>
|
22
|
+
<% if request_data[:user_agent].present? %>
|
23
|
+
User Agent: <%= request_data[:user_agent] %>
|
24
|
+
<% end %>
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require_relative './core'
|
2
|
+
module AsanaExceptionNotifier
|
3
|
+
module Request
|
4
|
+
# class used to make request in deferrable way
|
5
|
+
class Client
|
6
|
+
include AsanaExceptionNotifier::Request::Core
|
7
|
+
include EM::Deferrable
|
8
|
+
|
9
|
+
attr_reader :url, :options, :api_key, :request_name, :request_final, :action
|
10
|
+
|
11
|
+
def initialize(api_key, url, options, &callback)
|
12
|
+
@api_key = api_key
|
13
|
+
@url = url
|
14
|
+
|
15
|
+
@options = options.symbolize_keys
|
16
|
+
@request_name = @options.fetch(:request_name, '')
|
17
|
+
@request_final = @options.fetch(:request_final, false)
|
18
|
+
@action = @options.fetch(:action, '')
|
19
|
+
|
20
|
+
self.callback(&callback)
|
21
|
+
|
22
|
+
send_request_and_rescue
|
23
|
+
end
|
24
|
+
|
25
|
+
def multi_manager
|
26
|
+
@multi_manager ||= options.fetch(:multi_manager, nil)
|
27
|
+
end
|
28
|
+
|
29
|
+
def em_request_options
|
30
|
+
request = setup_em_options(@options).delete(:em_request)
|
31
|
+
params = {
|
32
|
+
head: (request[:head] || {}).merge(
|
33
|
+
'Authorization' => "Bearer #{@api_key}"
|
34
|
+
),
|
35
|
+
body: request[:body]
|
36
|
+
}
|
37
|
+
super(params)
|
38
|
+
end
|
39
|
+
|
40
|
+
def send_request_and_rescue
|
41
|
+
@http = em_request(@url, @options)
|
42
|
+
send_request
|
43
|
+
rescue => exception
|
44
|
+
log_exception(exception)
|
45
|
+
fail(result: { message: exception })
|
46
|
+
end
|
47
|
+
|
48
|
+
def send_request
|
49
|
+
fetch_data(@options) do |http_response|
|
50
|
+
handle_all_responses(http_response)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def handle_all_responses(http_response)
|
55
|
+
@multi_manager.requests.delete(@http) if @multi_manager.present?
|
56
|
+
if http_response.is_a?(Hash) && %i(callback errback).all? { |key| http_response.symbolize_keys.keys.include?(key) }
|
57
|
+
handle_multi_response(http_response)
|
58
|
+
else
|
59
|
+
handle_response(http_response, @options.fetch(:request_name, ''))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def handle_multi_response(http_response)
|
64
|
+
logger.debug('[AsanaExceptionNotifier]: Handling multi responses')
|
65
|
+
get_multi_request_values(http_response, :callback).each { |request_name, response| handle_response(response, request_name) }
|
66
|
+
get_multi_request_values(http_response, :errback).each { |request_name, response| handle_error(response, request_name) }
|
67
|
+
end
|
68
|
+
|
69
|
+
def handle_error(error, key = '')
|
70
|
+
logger.debug("[AsanaExceptionNotifier]: Task #{key} #{@action} returned: #{error}")
|
71
|
+
fail(error)
|
72
|
+
end
|
73
|
+
|
74
|
+
def handle_response(http_response, key = '')
|
75
|
+
logger.debug("[AsanaExceptionNotifier]: Task #{key} #{@action} returned: #{http_response}")
|
76
|
+
data = JSON.parse(http_response)
|
77
|
+
callback_task_creation(data)
|
78
|
+
end
|
79
|
+
|
80
|
+
def callback_task_creation(data)
|
81
|
+
data.fetch('errors', {}).present? ? handle_error(data) : succeed(data)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|