Commit 0945dadc53892062df59f624ecce7b0429696cca

Authored by Michał Młoźniak
1 parent 3c73874d
Exists in master and in 1 other branch production

Rename JsonParser to AirbrakeApi::V3::NoticeParser

Also I've added additional spec for requests with api_key set to nil.
app/controllers/api/v3/notices_controller.rb
@@ -9,7 +9,7 @@ class Api::V3::NoticesController < ApplicationController @@ -9,7 +9,7 @@ class Api::V3::NoticesController < ApplicationController
9 response.headers['Access-Control-Allow-Headers'] = 'origin, content-type, accept' 9 response.headers['Access-Control-Allow-Headers'] = 'origin, content-type, accept'
10 10
11 if !request.options? 11 if !request.options?
12 - report = JsonParser.new(params).report 12 + report = AirbrakeApi::V3::NoticeParser.new(params).report
13 13
14 if report.valid? 14 if report.valid?
15 if report.should_keep? 15 if report.should_keep?
@@ -28,7 +28,7 @@ class Api::V3::NoticesController < ApplicationController @@ -28,7 +28,7 @@ class Api::V3::NoticesController < ApplicationController
28 else 28 else
29 render nothing: true 29 render nothing: true
30 end 30 end
31 - rescue JsonParser::ParamsError 31 + rescue AirbrakeApi::ParamsError
32 render text: 'Invalid request' 32 render text: 'Invalid request'
33 end 33 end
34 end 34 end
lib/airbrake_api/params_error.rb 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +module AirbrakeApi
  2 + class ParamsError < StandardError; end
  3 +end
0 \ No newline at end of file 4 \ No newline at end of file
lib/airbrake_api/v3/notice_parser.rb 0 → 100644
@@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
  1 +module AirbrakeApi
  2 + module V3
  3 + class NoticeParser
  4 + class ParamsError < StandardError; end
  5 +
  6 + attr_reader :params, :error
  7 +
  8 + def initialize(params)
  9 + @params = params || {}
  10 + end
  11 +
  12 + def report
  13 + attributes = {
  14 + error_class: error['type'],
  15 + message: error['message'],
  16 + backtrace: backtrace,
  17 + request: request,
  18 + server_environment: server_environment,
  19 + api_key: params['key'].present? ? params['key'] : params['project_id'],
  20 + notifier: params['notifier'],
  21 + user_attributes: user_attributes
  22 + }
  23 +
  24 + ErrorReport.new(attributes)
  25 + end
  26 +
  27 + private
  28 +
  29 + def error
  30 + raise ParamsError unless params.has_key?('errors') && params['errors'].any?
  31 + @error ||= params['errors'].first
  32 + end
  33 +
  34 + def backtrace
  35 + error['backtrace'].map do |backtrace_line|
  36 + {
  37 + method: backtrace_line['function'],
  38 + file: backtrace_line['file'],
  39 + number: backtrace_line['line'],
  40 + column: backtrace_line['column']
  41 + }
  42 + end
  43 + end
  44 +
  45 + def server_environment
  46 + {
  47 + 'environment-name' => context['environment'],
  48 + 'hostname' => hostname,
  49 + 'project-root' => context['rootDirectory'],
  50 + 'app-version' => context['version']
  51 + }
  52 + end
  53 +
  54 + def request
  55 + environment = params['environment'].merge(
  56 + 'HTTP_USER_AGENT' => context['userAgent']
  57 + )
  58 +
  59 + {
  60 + 'cgi-data' => environment,
  61 + 'session' => params['session'],
  62 + 'params' => params['params'],
  63 + 'url' => url,
  64 + 'component' => context['component'],
  65 + 'action' => context['action']
  66 + }
  67 + end
  68 +
  69 + def user_attributes
  70 + hash = context.slice('userId', 'userUsername', 'userName', 'userEmail')
  71 + Hash[hash.map { |key, value| [key.sub(/^user/, ''), value] }]
  72 + end
  73 +
  74 + def url
  75 + context['url']
  76 + end
  77 +
  78 + def hostname
  79 + URI.parse(url).hostname
  80 + rescue URI::InvalidURIError
  81 + ''
  82 + end
  83 +
  84 + def context
  85 + @context = params['context'] || {}
  86 + end
  87 + end
  88 + end
  89 +end
0 \ No newline at end of file 90 \ No newline at end of file
lib/json_parser.rb
@@ -1,85 +0,0 @@ @@ -1,85 +0,0 @@
1 -class JsonParser  
2 - class ParamsError < StandardError; end  
3 -  
4 - attr_reader :params, :error  
5 -  
6 - def initialize(params)  
7 - @params = params || {}  
8 - end  
9 -  
10 - def report  
11 - attributes = {  
12 - error_class: error['type'],  
13 - message: error['message'],  
14 - backtrace: backtrace,  
15 - request: request,  
16 - server_environment: server_environment,  
17 - api_key: params['project_id'] || params['key'],  
18 - notifier: params['notifier'],  
19 - user_attributes: user_attributes  
20 - }  
21 -  
22 - ErrorReport.new(attributes)  
23 - end  
24 -  
25 - private  
26 -  
27 - def error  
28 - raise ParamsError unless params.has_key?('errors') && params['errors'].any?  
29 - @error ||= params['errors'].first  
30 - end  
31 -  
32 - def backtrace  
33 - error['backtrace'].map do |backtrace_line|  
34 - {  
35 - method: backtrace_line['function'],  
36 - file: backtrace_line['file'],  
37 - number: backtrace_line['line'],  
38 - column: backtrace_line['column']  
39 - }  
40 - end  
41 - end  
42 -  
43 - def server_environment  
44 - {  
45 - 'environment-name' => context['environment'],  
46 - 'hostname' => hostname,  
47 - 'project-root' => context['rootDirectory'],  
48 - 'app-version' => context['version']  
49 - }  
50 - end  
51 -  
52 - def request  
53 - environment = params['environment'].merge(  
54 - 'HTTP_USER_AGENT' => context['userAgent']  
55 - )  
56 -  
57 - {  
58 - 'cgi-data' => environment,  
59 - 'session' => params['session'],  
60 - 'params' => params['params'],  
61 - 'url' => url,  
62 - 'component' => context['component'],  
63 - 'action' => context['action']  
64 - }  
65 - end  
66 -  
67 - def user_attributes  
68 - hash = context.slice('userId', 'userUsername', 'userName', 'userEmail')  
69 - Hash[hash.map { |key, value| [key.sub(/^user/, ''), value] }]  
70 - end  
71 -  
72 - def url  
73 - context['url']  
74 - end  
75 -  
76 - def hostname  
77 - URI.parse(url).hostname  
78 - rescue URI::InvalidURIError  
79 - ''  
80 - end  
81 -  
82 - def context  
83 - @context = params['context'] || {}  
84 - end  
85 -end  
86 \ No newline at end of file 0 \ No newline at end of file
spec/lib/airbrake_api/v3/notice_parser_spec.rb 0 → 100644
@@ -0,0 +1,81 @@ @@ -0,0 +1,81 @@
  1 +describe AirbrakeApi::V3::NoticeParser do
  2 + let(:app) { Fabricate(:app) }
  3 +
  4 + it 'raises error when errors attribute is missing' do
  5 + expect {
  6 + AirbrakeApi::V3::NoticeParser.new({}).report
  7 + }.to raise_error(AirbrakeApi::V3::NoticeParser::ParamsError)
  8 +
  9 + expect {
  10 + AirbrakeApi::V3::NoticeParser.new({'errors' => []}).report
  11 + }.to raise_error(AirbrakeApi::V3::NoticeParser::ParamsError)
  12 + end
  13 +
  14 + it 'parses JSON payload and returns ErrorReport' do
  15 + params = build_params(api_key: app.api_key)
  16 +
  17 + report = AirbrakeApi::V3::NoticeParser.new(params).report
  18 + notice = report.generate_notice!
  19 +
  20 + expect(report.error_class).to eq('Error')
  21 + expect(report.message).to eq('Error: TestError')
  22 + expect(report.backtrace.lines.size).to eq(9)
  23 + expect(notice.user_attributes).to include({'Id' => 1, 'Name' => 'John Doe', 'Email' => 'john.doe@example.org', 'Username' => 'john'})
  24 + expect(notice.session).to include('isAdmin' => true)
  25 + expect(notice.params).to include('returnTo' => 'dashboard')
  26 + expect(notice.env_vars).to include(
  27 + 'navigator_vendor' => 'Google Inc.',
  28 + 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36'
  29 + )
  30 + end
  31 +
  32 + it 'parses JSON payload when api_key is missing but project_id is present' do
  33 + params = build_params(api_key: nil, project_id: app.api_key)
  34 +
  35 + report = AirbrakeApi::V3::NoticeParser.new(params).report
  36 + expect(report).to be_valid
  37 + end
  38 +
  39 + def build_params(options = {})
  40 + JSON.parse(<<-EOL)
  41 + {
  42 + "notifier":{"name":"airbrake-js-v8","version":"0.3.10","url":"https://github.com/airbrake/airbrake-js"},
  43 + "errors":[
  44 + {
  45 + "type":"Error",
  46 + "message":"Error: TestError",
  47 + "backtrace":[
  48 + {"function":"d","file":"http://localhost:3000/assets/application.js","line":11234,"column":24},
  49 + {"function":"c","file":"http://localhost:3000/assets/application.js","line":11233,"column":18},
  50 + {"function":"b","file":"http://localhost:3000/assets/application.js","line":11232,"column":18},
  51 + {"function":"a","file":"http://localhost:3000/assets/application.js","line":11231,"column":18},
  52 + {"function":"HTMLDocument.<anonymous>","file":"http://localhost:3000/assets/application.js","line":11236,"column":3},
  53 + {"function":"fire","file":"http://localhost:3000/assets/application.js","line":1018,"column":34},
  54 + {"function":"Object.self.fireWith [as resolveWith]","file":"http://localhost:3000/assets/application.js","line":1128,"column":13},
  55 + {"function":"Function.jQuery.extend.ready","file":"http://localhost:3000/assets/application.js","line":417,"column":15},
  56 + {"function":"HTMLDocument.DOMContentLoaded","file":"http://localhost:3000/assets/application.js","line":93,"column":14}
  57 + ]
  58 + }
  59 + ],
  60 + "context":{
  61 + "language":"JavaScript",
  62 + "sourceMapEnabled":true,
  63 + "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36",
  64 + "url":"http://localhost:3000/kontakt",
  65 + "userId":1,"userUsername":"john",
  66 + "userName":"John Doe",
  67 + "userUsername": "john",
  68 + "userEmail":"john.doe@example.org",
  69 + "version":"1.0",
  70 + "component":"ContactsController",
  71 + "action":"show"
  72 + },
  73 + "params":{"returnTo":"dashboard"},
  74 + "environment":{"navigator_vendor":"Google Inc."},
  75 + "session":{"isAdmin":true},
  76 + "key":"#{options[:api_key]}",
  77 + "project_id":"#{options[:project_id]}"
  78 + }
  79 + EOL
  80 + end
  81 +end
0 \ No newline at end of file 82 \ No newline at end of file
spec/lib/json_parser_spec.rb
@@ -1,69 +0,0 @@ @@ -1,69 +0,0 @@
1 -describe JsonParser do  
2 - let(:app) { Fabricate(:app) }  
3 -  
4 - it 'raises error when errors attribute is missing' do  
5 - expect {  
6 - JsonParser.new({}).report  
7 - }.to raise_error(JsonParser::ParamsError)  
8 -  
9 - expect {  
10 - JsonParser.new({'errors' => []}).report  
11 - }.to raise_error(JsonParser::ParamsError)  
12 - end  
13 -  
14 - it 'parses JSON payload and returns ErrorReport' do  
15 - params = JSON.parse(<<-EOL)  
16 - {  
17 - "notifier":{"name":"airbrake-js-v8","version":"0.3.10","url":"https://github.com/airbrake/airbrake-js"},  
18 - "errors":[  
19 - {  
20 - "type":"Error",  
21 - "message":"Error: TestError",  
22 - "backtrace":[  
23 - {"function":"d","file":"http://localhost:3000/assets/application.js","line":11234,"column":24},  
24 - {"function":"c","file":"http://localhost:3000/assets/application.js","line":11233,"column":18},  
25 - {"function":"b","file":"http://localhost:3000/assets/application.js","line":11232,"column":18},  
26 - {"function":"a","file":"http://localhost:3000/assets/application.js","line":11231,"column":18},  
27 - {"function":"HTMLDocument.<anonymous>","file":"http://localhost:3000/assets/application.js","line":11236,"column":3},  
28 - {"function":"fire","file":"http://localhost:3000/assets/application.js","line":1018,"column":34},  
29 - {"function":"Object.self.fireWith [as resolveWith]","file":"http://localhost:3000/assets/application.js","line":1128,"column":13},  
30 - {"function":"Function.jQuery.extend.ready","file":"http://localhost:3000/assets/application.js","line":417,"column":15},  
31 - {"function":"HTMLDocument.DOMContentLoaded","file":"http://localhost:3000/assets/application.js","line":93,"column":14}  
32 - ]  
33 - }  
34 - ],  
35 - "context":{  
36 - "language":"JavaScript",  
37 - "sourceMapEnabled":true,  
38 - "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36",  
39 - "url":"http://localhost:3000/kontakt",  
40 - "userId":1,"userUsername":"john",  
41 - "userName":"John Doe",  
42 - "userUsername": "john",  
43 - "userEmail":"john.doe@example.org",  
44 - "version":"1.0",  
45 - "component":"ContactsController",  
46 - "action":"show"  
47 - },  
48 - "params":{"returnTo":"dashboard"},  
49 - "environment":{"navigator_vendor":"Google Inc."},  
50 - "session":{"isAdmin":true},  
51 - "key":"#{app.api_key}"  
52 - }  
53 - EOL  
54 -  
55 - report = JsonParser.new(params).report  
56 - notice = report.generate_notice!  
57 -  
58 - expect(report.error_class).to eq('Error')  
59 - expect(report.message).to eq('Error: TestError')  
60 - expect(report.backtrace.lines.size).to eq(9)  
61 - expect(notice.user_attributes).to include({'Id' => 1, 'Name' => 'John Doe', 'Email' => 'john.doe@example.org', 'Username' => 'john'})  
62 - expect(notice.session).to include('isAdmin' => true)  
63 - expect(notice.params).to include('returnTo' => 'dashboard')  
64 - expect(notice.env_vars).to include(  
65 - 'navigator_vendor' => 'Google Inc.',  
66 - 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.99 Safari/537.36'  
67 - )  
68 - end  
69 -end  
70 \ No newline at end of file 0 \ No newline at end of file