Commit 0945dadc53892062df59f624ecce7b0429696cca
1 parent
3c73874d
Exists in
master
and in
1 other branch
Rename JsonParser to AirbrakeApi::V3::NoticeParser
Also I've added additional spec for requests with api_key set to nil.
Showing
6 changed files
with
175 additions
and
156 deletions
Show diff stats
app/controllers/api/v3/notices_controller.rb
| ... | ... | @@ -9,7 +9,7 @@ class Api::V3::NoticesController < ApplicationController |
| 9 | 9 | response.headers['Access-Control-Allow-Headers'] = 'origin, content-type, accept' |
| 10 | 10 | |
| 11 | 11 | if !request.options? |
| 12 | - report = JsonParser.new(params).report | |
| 12 | + report = AirbrakeApi::V3::NoticeParser.new(params).report | |
| 13 | 13 | |
| 14 | 14 | if report.valid? |
| 15 | 15 | if report.should_keep? |
| ... | ... | @@ -28,7 +28,7 @@ class Api::V3::NoticesController < ApplicationController |
| 28 | 28 | else |
| 29 | 29 | render nothing: true |
| 30 | 30 | end |
| 31 | - rescue JsonParser::ParamsError | |
| 31 | + rescue AirbrakeApi::ParamsError | |
| 32 | 32 | render text: 'Invalid request' |
| 33 | 33 | end |
| 34 | 34 | end | ... | ... |
| ... | ... | @@ -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 | 90 | \ No newline at end of file | ... | ... |
lib/json_parser.rb
| ... | ... | @@ -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 | 0 | \ No newline at end of file |
| ... | ... | @@ -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 | 82 | \ No newline at end of file | ... | ... |
spec/lib/json_parser_spec.rb
| ... | ... | @@ -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 | 0 | \ No newline at end of file |