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 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
... ...
lib/airbrake_api/params_error.rb 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +module AirbrakeApi
  2 + class ParamsError < StandardError; end
  3 +end
0 4 \ No newline at end of file
... ...
lib/airbrake_api/v3/notice_parser.rb 0 → 100644
... ... @@ -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
spec/lib/airbrake_api/v3/notice_parser_spec.rb 0 → 100644
... ... @@ -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