crashbreak 0.0.2 → 0.9.0
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/crashbreak.gemspec +3 -0
- data/lib/crashbreak.rb +16 -2
- data/lib/crashbreak/AWS.rb +30 -0
- data/lib/crashbreak/config/configurator.rb +29 -0
- data/lib/crashbreak/dumpers_data_repository.rb +34 -0
- data/lib/crashbreak/exception_notifier.rb +14 -4
- data/lib/crashbreak/exceptions_repository.rb +24 -4
- data/lib/crashbreak/github_integration_service.rb +65 -0
- data/lib/crashbreak/request_parser.rb +33 -0
- data/lib/crashbreak/version.rb +1 -1
- data/lib/dumpers/database_dumper.rb +39 -0
- data/lib/dumpers/request_dumper.rb +15 -0
- data/lib/generators/crashbreak/templates/test.rb +11 -0
- data/lib/generators/crashbreak/test_generator.rb +12 -0
- data/lib/restorers/database_restorer.rb +35 -0
- data/lib/restorers/request_restorer.rb +9 -0
- data/lib/restorers/state_restorer.rb +37 -0
- data/lib/tasks/crashbreak.rake +14 -0
- data/spec/dumpers/request_dumper_spec.rb +14 -0
- data/spec/features/sending_error_report_spec.rb +6 -4
- data/spec/lib/dumpers_data_repository_spec.rb +19 -0
- data/spec/lib/exception_notifier_spec.rb +36 -2
- data/spec/lib/exceptions_repository_spec.rb +12 -2
- data/spec/lib/generators/test_generator_spec.rb +16 -0
- data/spec/lib/github_integration_service_spec.rb +47 -0
- data/spec/lib/request_sender_spec.rb +33 -0
- data/spec/restorers/state_restorer_spec.rb +29 -0
- metadata +67 -8
- data/lib/dumpers/program_name_dumper.rb +0 -16
- data/lib/restorers/program_name_restorer.rb +0 -7
- data/spec/dumpers/program_name_dumper_spec.rb +0 -12
- data/spec/restorers/program_name_restorer_spec.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29371eea672de29bc9369c0280e99a3c16d08ca8
|
4
|
+
data.tar.gz: 432c7a98e58df84245f5b4f27a8aa5cfb541969c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 115c2e65b82096eb44a8550a4dbdb6a48a52f05e0358d4913b57a9f1d4e0a40b5b66079aebb4403eb2d646bc881d28b8081478643327ca87346f163a3ec86784
|
7
|
+
data.tar.gz: 8046ae60fb3f1722f9c326e932a36901c86612e9a1161b4a06abf7fbb1be8910558705985cf8bdd4797981d808b3095a4312cbdee81a4eacc8ae75658760a218
|
data/crashbreak.gemspec
CHANGED
@@ -22,7 +22,10 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_development_dependency 'rails'
|
23
23
|
spec.add_development_dependency 'webmock'
|
24
24
|
spec.add_development_dependency 'simplecov'
|
25
|
+
spec.add_development_dependency 'deepstruct'
|
25
26
|
|
26
27
|
spec.add_runtime_dependency 'faraday', '>= 0'
|
27
28
|
spec.add_runtime_dependency 'request_store', '>= 0'
|
29
|
+
spec.add_runtime_dependency 'octokit', '>= 0'
|
30
|
+
spec.add_runtime_dependency 'aws-sdk', '~> 2'
|
28
31
|
end
|
data/lib/crashbreak.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'rails'
|
2
2
|
require 'faraday'
|
3
3
|
require 'request_store'
|
4
|
+
require 'octokit'
|
5
|
+
require 'aws-sdk'
|
4
6
|
require 'crashbreak/version'
|
5
7
|
require 'crashbreak/exception_notifier'
|
6
8
|
require 'crashbreak/formatters/basic_formatter'
|
@@ -13,9 +15,17 @@ require 'crashbreak/config/configurator'
|
|
13
15
|
require 'crashbreak/config/configurable'
|
14
16
|
require 'crashbreak/exception_catcher_middleware'
|
15
17
|
require 'crashbreak/exceptions_repository'
|
18
|
+
require 'crashbreak/dumpers_data_repository'
|
19
|
+
require 'crashbreak/request_parser'
|
20
|
+
require 'crashbreak/github_integration_service'
|
21
|
+
require 'crashbreak/aws'
|
16
22
|
|
17
|
-
require 'dumpers/
|
18
|
-
require '
|
23
|
+
require 'dumpers/database_dumper'
|
24
|
+
require 'dumpers/request_dumper'
|
25
|
+
|
26
|
+
require 'restorers/database_restorer'
|
27
|
+
require 'restorers/state_restorer'
|
28
|
+
require 'restorers/request_restorer'
|
19
29
|
|
20
30
|
module Crashbreak
|
21
31
|
extend Configurable
|
@@ -29,4 +39,8 @@ module Crashbreak
|
|
29
39
|
load 'tasks/crashbreak.rake'
|
30
40
|
end
|
31
41
|
end
|
42
|
+
|
43
|
+
def self.root
|
44
|
+
File.expand_path '../..', __FILE__
|
45
|
+
end
|
32
46
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Crashbreak
|
2
|
+
module AWS
|
3
|
+
def client
|
4
|
+
@client ||= Aws::S3::Client.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def bucket_name
|
8
|
+
Crashbreak.configure.dumper_options[:aws_bucket_name]
|
9
|
+
end
|
10
|
+
|
11
|
+
def aws_region
|
12
|
+
Crashbreak.configure.dumper_options[:aws_region] || ENV['AWS_REGION']
|
13
|
+
end
|
14
|
+
|
15
|
+
def aws_key_id
|
16
|
+
Crashbreak.configure.dumper_options[:aws_access_key_id] || ENV['AWS_ACCESS_KEY_ID']
|
17
|
+
end
|
18
|
+
|
19
|
+
def aws_secret_key
|
20
|
+
Crashbreak.configure.dumper_options[:aws_secret_access_key] || ENV['AWS_SECRET_ACCESS_KEY']
|
21
|
+
end
|
22
|
+
|
23
|
+
def prepare_aws
|
24
|
+
Aws.config.update(
|
25
|
+
credentials: Aws::Credentials.new(aws_key_id, aws_secret_key),
|
26
|
+
s3: { region: aws_region }
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -3,6 +3,15 @@ module Crashbreak
|
|
3
3
|
attr_accessor :api_key
|
4
4
|
attr_accessor :exception_notifier
|
5
5
|
attr_accessor :error_serializers
|
6
|
+
attr_accessor :dumpers
|
7
|
+
attr_accessor :dumper_options
|
8
|
+
attr_accessor :restorer_options
|
9
|
+
|
10
|
+
attr_accessor :github_login
|
11
|
+
attr_accessor :github_password
|
12
|
+
attr_accessor :github_repo_name
|
13
|
+
attr_accessor :github_spec_file_path
|
14
|
+
attr_accessor :github_development_branch
|
6
15
|
|
7
16
|
def exception_notifier
|
8
17
|
@exception_notifier || ExceptionNotifier.new
|
@@ -11,5 +20,25 @@ module Crashbreak
|
|
11
20
|
def error_serializers
|
12
21
|
@error_serializers || []
|
13
22
|
end
|
23
|
+
|
24
|
+
def github_spec_file_path
|
25
|
+
@github_spec_file_path || 'spec/crashbreak_error_spec.rb'
|
26
|
+
end
|
27
|
+
|
28
|
+
def github_development_branch
|
29
|
+
@github_development_branch || 'master'
|
30
|
+
end
|
31
|
+
|
32
|
+
def dumpers
|
33
|
+
@dumpers || []
|
34
|
+
end
|
35
|
+
|
36
|
+
def dumper_options
|
37
|
+
@dumper_options || {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def restorer_options
|
41
|
+
@restorer_options || {}
|
42
|
+
end
|
14
43
|
end
|
15
44
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Crashbreak
|
2
|
+
class DumpersDataRepository
|
3
|
+
BASE_URL = 'http://crashbreak.herokuapp.com/api'
|
4
|
+
|
5
|
+
def initialize(error_id)
|
6
|
+
@error_id = error_id
|
7
|
+
end
|
8
|
+
|
9
|
+
def dumpers_data
|
10
|
+
JSON.parse request_body
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def request_body
|
16
|
+
connection.get do |req|
|
17
|
+
req.url request_url
|
18
|
+
req.headers['Content-Type'] = 'application/json'
|
19
|
+
end.body
|
20
|
+
end
|
21
|
+
|
22
|
+
def connection
|
23
|
+
Faraday.new(url: request_url)
|
24
|
+
end
|
25
|
+
|
26
|
+
def request_url
|
27
|
+
"#{BASE_URL}/projects/#{project_token}/errors/#{@error_id}/dumpers_data"
|
28
|
+
end
|
29
|
+
|
30
|
+
def project_token
|
31
|
+
Crashbreak.configure.api_key
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,19 +1,29 @@
|
|
1
1
|
module Crashbreak
|
2
2
|
class ExceptionNotifier
|
3
3
|
def notify
|
4
|
-
exceptions_repository.create serialize_exception
|
4
|
+
error_id = exceptions_repository.create serialize_exception
|
5
|
+
GithubIntegrationService.new(error_id).push_test if Crashbreak.configure.github_repo_name.present?
|
5
6
|
end
|
6
7
|
|
7
8
|
private
|
8
9
|
|
9
10
|
def serialize_exception
|
10
11
|
{}.tap do |exception_hash|
|
11
|
-
serializers.each
|
12
|
-
|
13
|
-
end
|
12
|
+
serializers.each { |serializer| exception_hash.deep_merge!(serializer.serialize) }
|
13
|
+
exception_hash[:dumpers_data] = dumpers_data
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
def dumpers_data
|
18
|
+
dumpers.map do |dumper|
|
19
|
+
[dumper.class.to_s, dumper.dump]
|
20
|
+
end.to_h
|
21
|
+
end
|
22
|
+
|
23
|
+
def dumpers
|
24
|
+
Crashbreak.configure.dumpers
|
25
|
+
end
|
26
|
+
|
17
27
|
def serializers
|
18
28
|
[BasicInformationFormatter.new] + Crashbreak.configure.error_serializers
|
19
29
|
end
|
@@ -3,23 +3,43 @@ module Crashbreak
|
|
3
3
|
BASE_URL = 'http://crashbreak.herokuapp.com/api'
|
4
4
|
|
5
5
|
def create(error_report_hash)
|
6
|
+
JSON.parse(post_request(error_report_hash).body)['id']
|
7
|
+
end
|
8
|
+
|
9
|
+
def resolve(error_id)
|
10
|
+
resolve_request error_id
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def post_request(error_report_hash)
|
6
16
|
connection.post do |req|
|
7
|
-
req.url
|
17
|
+
req.url create_request_url
|
8
18
|
req.body = error_report_hash.to_json
|
9
19
|
req.headers['Content-Type'] = 'application/json'
|
10
20
|
end
|
11
21
|
end
|
12
22
|
|
13
|
-
|
23
|
+
def resolve_request(error_id)
|
24
|
+
connection.put do |req|
|
25
|
+
req.url resolve_request_url(error_id)
|
26
|
+
req.body = { error_report: { status: :resolved } }.to_json
|
27
|
+
req.headers['Content-Type'] = 'application/json'
|
28
|
+
end
|
29
|
+
end
|
14
30
|
|
15
31
|
def connection
|
16
|
-
Faraday.new
|
32
|
+
Faraday.new
|
17
33
|
end
|
18
34
|
|
19
|
-
def
|
35
|
+
def create_request_url
|
20
36
|
"#{BASE_URL}/projects/#{project_token}/errors"
|
21
37
|
end
|
22
38
|
|
39
|
+
def resolve_request_url(error_id)
|
40
|
+
"#{BASE_URL}/projects/#{project_token}/errors/#{error_id}"
|
41
|
+
end
|
42
|
+
|
23
43
|
def project_token
|
24
44
|
Crashbreak.configure.api_key
|
25
45
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Crashbreak
|
2
|
+
class GithubIntegrationService
|
3
|
+
def initialize(error_id)
|
4
|
+
@error_id = error_id
|
5
|
+
end
|
6
|
+
|
7
|
+
def push_test
|
8
|
+
create_branch(branch_sha)
|
9
|
+
create_test_file
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove_test
|
13
|
+
client.delete_contents(repo_name, file_path, "Remove test for error #{@error_id}",
|
14
|
+
test_file_sha, branch: "refs/#{error_branch_name}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_pull_request
|
18
|
+
client.create_pull_request(repo_name, development_branch_name, error_branch_name.gsub('heads/', ''), "Crashbreak - fix for error #{@error_id}", '')
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def branch_sha
|
24
|
+
@branch_sha ||= client.ref(repo_name, 'heads/master').object.sha
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_file_sha
|
28
|
+
@test_file_sha ||= client.contents(repo_name, path: file_path, ref: error_branch_name).sha
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_branch(sha)
|
32
|
+
client.create_ref repo_name, error_branch_name, sha
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_test_file
|
36
|
+
client.create_contents repo_name, file_path, "Add test file for error #{@error_id}",
|
37
|
+
file_content, { branch: "refs/#{error_branch_name}" }
|
38
|
+
end
|
39
|
+
|
40
|
+
def file_content
|
41
|
+
test_file_content = File.read("#{Crashbreak.root}/lib/generators/crashbreak/templates/test.rb")
|
42
|
+
test_file_content.gsub('<%= error_id %>', @error_id.to_s)
|
43
|
+
end
|
44
|
+
|
45
|
+
def client
|
46
|
+
@client ||= Octokit::Client.new(login: Crashbreak.configure.github_login, password: Crashbreak.configure.github_password)
|
47
|
+
end
|
48
|
+
|
49
|
+
def repo_name
|
50
|
+
Crashbreak.configure.github_repo_name
|
51
|
+
end
|
52
|
+
|
53
|
+
def error_branch_name
|
54
|
+
"heads/crashbreak-error-#{@error_id}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def file_path
|
58
|
+
Crashbreak.configure.github_spec_file_path
|
59
|
+
end
|
60
|
+
|
61
|
+
def development_branch_name
|
62
|
+
Crashbreak.configure.github_development_branch
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Crashbreak
|
2
|
+
class RequestParser
|
3
|
+
def initialize(request_data)
|
4
|
+
@request = request_data
|
5
|
+
end
|
6
|
+
|
7
|
+
def request_data
|
8
|
+
yield(request_method_sym, request_path, request_body, request_headers)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def request_method_sym
|
14
|
+
@request['REQUEST_METHOD'].underscore.to_sym
|
15
|
+
end
|
16
|
+
|
17
|
+
def request_path
|
18
|
+
@request['PATH_INFO']
|
19
|
+
end
|
20
|
+
|
21
|
+
def request_body
|
22
|
+
JSON.parse(request_hash_as_string.gsub('=>', ':')).symbolize_keys
|
23
|
+
end
|
24
|
+
|
25
|
+
def request_hash_as_string
|
26
|
+
@request['action_dispatch.request.request_parameters'] || '{}'
|
27
|
+
end
|
28
|
+
|
29
|
+
def request_headers
|
30
|
+
@request.select{|key| key.start_with?('HTTP_')}.map{|key, value| [key.gsub('HTTP_', ''), value]}.to_h
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/crashbreak/version.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Crashbreak
|
2
|
+
class DatabaseDumper
|
3
|
+
include Crashbreak::AWS
|
4
|
+
|
5
|
+
def dump
|
6
|
+
dump_database
|
7
|
+
prepare_aws
|
8
|
+
upload_dump
|
9
|
+
remove_locally_dump
|
10
|
+
{ file_name: aws_file_name }
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def dump_database
|
16
|
+
system(dump_command)
|
17
|
+
end
|
18
|
+
|
19
|
+
def upload_dump
|
20
|
+
client.put_object(bucket: bucket_name, key: aws_file_name, body: File.read(dump_location))
|
21
|
+
end
|
22
|
+
|
23
|
+
def remove_locally_dump
|
24
|
+
File.delete(dump_location)
|
25
|
+
end
|
26
|
+
|
27
|
+
def aws_file_name
|
28
|
+
@aws_file_name ||= "Crashbreak - database dump #{DateTime.now.utc}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def dump_location
|
32
|
+
Crashbreak.configure.dumper_options[:dump_location]
|
33
|
+
end
|
34
|
+
|
35
|
+
def dump_command
|
36
|
+
Crashbreak.configure.dumper_options[:dump_command]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
describe 'error id: <%= error_id %>', type: :request do
|
2
|
+
|
3
|
+
let(:restorers_data) { StateRestorer.new('<%= error_id %>').restore }
|
4
|
+
let(:request_parser) { Crashbreak::RequestParser.new restorers_data[:request] }
|
5
|
+
|
6
|
+
it 'sends request' do
|
7
|
+
request_parser.request_data do |request_method, url, body, headers|
|
8
|
+
method(request_method).call url, body, headers
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Crashbreak
|
2
|
+
module Generators
|
3
|
+
class TestGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
5
|
+
argument :error_id, type: :string
|
6
|
+
|
7
|
+
def create_test_file
|
8
|
+
template 'test.rb', 'spec/error_request_spec.rb'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Crashbreak
|
2
|
+
class DatabaseRestorer
|
3
|
+
include AWS
|
4
|
+
|
5
|
+
def initialize(dump_data)
|
6
|
+
@file_name = dump_data['file_name']
|
7
|
+
end
|
8
|
+
|
9
|
+
def restore
|
10
|
+
system('dropdb crashbreak-test')
|
11
|
+
system('createdb -T template0 crashbreak-test')
|
12
|
+
prepare_aws
|
13
|
+
download_dump
|
14
|
+
restore_database
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def restore_database
|
20
|
+
system(restore_command)
|
21
|
+
end
|
22
|
+
|
23
|
+
def download_dump
|
24
|
+
File.open("#{Rails.root}/tmp/db.dump", 'wb') do |file|
|
25
|
+
client.get_object(bucket: bucket_name, key: @file_name) do |data|
|
26
|
+
file.write(data)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def restore_command
|
32
|
+
Crashbreak.configure.restorer_options[:restore_command]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class StateRestorer
|
2
|
+
|
3
|
+
def initialize(error_id)
|
4
|
+
@error_id = error_id
|
5
|
+
end
|
6
|
+
|
7
|
+
def restore
|
8
|
+
{}.tap do |restorers_hash|
|
9
|
+
restorers.each do |restorer|
|
10
|
+
restorers_hash[key_name(restorer)] = restorer.new(dumper_data(restorer)).restore
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def dumpers_data
|
18
|
+
@dumpers_data ||= Crashbreak::DumpersDataRepository.new(@error_id).dumpers_data
|
19
|
+
end
|
20
|
+
|
21
|
+
def restorers
|
22
|
+
dumpers_data.keys.map {|dumper_name| restorer_class_by_dumper_name(dumper_name)}
|
23
|
+
end
|
24
|
+
|
25
|
+
def restorer_class_by_dumper_name(dumper_name)
|
26
|
+
dumper_name.gsub('Dumper', 'Restorer').constantize
|
27
|
+
end
|
28
|
+
|
29
|
+
def dumper_data(restorer)
|
30
|
+
dumper_name = restorer.to_s.gsub('Restorer', 'Dumper')
|
31
|
+
dumpers_data[dumper_name]
|
32
|
+
end
|
33
|
+
|
34
|
+
def key_name(restorer)
|
35
|
+
restorer.to_s.gsub('Restorer', '').underscore.to_sym
|
36
|
+
end
|
37
|
+
end
|
data/lib/tasks/crashbreak.rake
CHANGED
@@ -10,6 +10,20 @@ namespace :crashbreak do
|
|
10
10
|
|
11
11
|
puts 'Done, now check if error exists on crashbreak.com!'
|
12
12
|
end
|
13
|
+
|
14
|
+
task resolve_error: :environment do
|
15
|
+
return puts 'error_id must be set (e.g rake crashbreak:resolve_error error_id=123)' if ENV['error_id'].nil?
|
16
|
+
service = Crashbreak::GithubIntegrationService.new ENV['error_id']
|
17
|
+
|
18
|
+
puts 'Removing test file from github...'
|
19
|
+
service.remove_test
|
20
|
+
|
21
|
+
puts 'Creating pull request'
|
22
|
+
service.create_pull_request
|
23
|
+
|
24
|
+
puts 'Resolving error in CrashBreak...'
|
25
|
+
Crashbreak::ExceptionsRepository.new.resolve ENV['error_id']
|
26
|
+
end
|
13
27
|
end
|
14
28
|
|
15
29
|
class CrashbreakTestError < StandardError
|
@@ -0,0 +1,14 @@
|
|
1
|
+
describe RequestDumper do
|
2
|
+
subject { described_class.new }
|
3
|
+
|
4
|
+
let(:example_request) { double(:request, env: Hash['REQUEST_URI' => 'example_uri']) }
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
RequestStore.store[:request] = example_request
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'returns a request hash' do
|
11
|
+
expect(subject.dump).to eq example_request.env
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -12,15 +12,16 @@ describe 'Sending error report to server' do
|
|
12
12
|
before(:each) do
|
13
13
|
Crashbreak.configure.api_key = project_token
|
14
14
|
Crashbreak.configure.error_serializers = [summary_formatter, Crashbreak::EnvironmentVariablesFormatter.new, TestErrorFormatter.new]
|
15
|
+
Crashbreak.configure.dumpers = [RequestDumper.new]
|
15
16
|
|
16
17
|
allow(crashing_app).to receive(:call).and_raise(example_error)
|
17
18
|
allow(example_error).to receive(:backtrace).and_return(%w(example backtrace))
|
18
|
-
|
19
|
+
allow_any_instance_of(Crashbreak::BasicFormatter).to receive(:request).and_return(example_request)
|
19
20
|
end
|
20
21
|
|
21
22
|
let!(:create_exception_request) do
|
22
23
|
stub_request(:post, "#{Crashbreak::ExceptionsRepository::BASE_URL}/projects/#{project_token}/errors").
|
23
|
-
with(body: error_report_hash.to_json).to_return(status: 200)
|
24
|
+
with(body: error_report_hash.to_json).to_return(status: 200, body: { id: 1 }.to_json)
|
24
25
|
end
|
25
26
|
|
26
27
|
let(:error_report_hash) do
|
@@ -28,7 +29,8 @@ describe 'Sending error report to server' do
|
|
28
29
|
name: example_error.to_s, message: example_error.message, backtrace: example_error.backtrace, environment: 'test',
|
29
30
|
summary: { action: example_request.env['PATH_INFO'], controller_name: example_controller.class.to_s,
|
30
31
|
file: example_error.backtrace[0], url: example_request.env['REQUEST_URI'], user_agent: example_request.env['HTTP_USER_AGENT'] },
|
31
|
-
additional_data: { environment: ENV.to_hash, test: { formatter: true } }
|
32
|
+
additional_data: { environment: ENV.to_hash, test: { formatter: true } },
|
33
|
+
dumpers_data: { 'RequestDumper' => example_request.env }
|
32
34
|
}
|
33
35
|
end
|
34
36
|
|
@@ -36,4 +38,4 @@ describe 'Sending error report to server' do
|
|
36
38
|
expect{ catching_middleware.call(env) }.to raise_error(TestError)
|
37
39
|
expect(create_exception_request).to have_been_made
|
38
40
|
end
|
39
|
-
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
describe Crashbreak::DumpersDataRepository do
|
2
|
+
subject { described_class.new error_id }
|
3
|
+
|
4
|
+
let(:error_id) { 1 }
|
5
|
+
let(:project_token) { 'example_project_token' }
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
Crashbreak.configure.api_key = project_token
|
9
|
+
|
10
|
+
stub_request(:get, "#{described_class::BASE_URL}/projects/#{project_token}/errors/#{error_id}/dumpers_data").
|
11
|
+
to_return(status: 200, body: dumpers_data.to_json)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:dumpers_data) { Hash['example_dumpers_data' => 'example'] }
|
15
|
+
|
16
|
+
it 'sends request to create exception report' do
|
17
|
+
expect(subject.dumpers_data).to eq dumpers_data
|
18
|
+
end
|
19
|
+
end
|
@@ -6,19 +6,53 @@ describe Crashbreak::ExceptionNotifier do
|
|
6
6
|
|
7
7
|
before(:each) do
|
8
8
|
allow(error).to receive(:backtrace).and_return(%w(example backtrace))
|
9
|
+
allow_any_instance_of(Crashbreak::Configurator).to receive(:error_serializers).and_return([])
|
10
|
+
allow_any_instance_of(described_class).to receive(:dumpers).and_return([])
|
11
|
+
allow_any_instance_of(Crashbreak::GithubIntegrationService).to receive(:push_test).and_return(true)
|
12
|
+
|
9
13
|
RequestStore[:exception] = error
|
14
|
+
RequestStore[:request] = double(:request, env: { request: :example_request_data } )
|
10
15
|
end
|
11
16
|
|
12
17
|
let(:exception_basic_hash) do
|
13
|
-
{ name: error_name, message: error_message, backtrace: error.backtrace, environment: ENV['RACK_ENV'] }
|
18
|
+
{ name: error_name, message: error_message, backtrace: error.backtrace, environment: ENV['RACK_ENV'], dumpers_data: {} }
|
14
19
|
end
|
15
20
|
|
16
21
|
context 'without additional serializers' do
|
17
|
-
before(:each) { allow_any_instance_of(Crashbreak::Configurator).to receive(:error_serializers).and_return([]) }
|
18
22
|
it 'sends pure error' do
|
19
23
|
expect_any_instance_of(Crashbreak::ExceptionsRepository).to receive(:create).with(exception_basic_hash)
|
20
24
|
subject.notify
|
21
25
|
end
|
26
|
+
|
27
|
+
context 'with dumpers' do
|
28
|
+
let(:expected_hash) { exception_basic_hash.merge(dumpers_hash) }
|
29
|
+
|
30
|
+
let(:dumpers_hash) do
|
31
|
+
{ dumpers_data: { 'RequestDumper' => { request: 'example_request_data' } } }
|
32
|
+
end
|
33
|
+
|
34
|
+
before(:each) do
|
35
|
+
allow_any_instance_of(described_class).to receive(:dumpers).and_return([RequestDumper.new])
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'sends dump data' do
|
39
|
+
expect_any_instance_of(Crashbreak::ExceptionsRepository).to receive(:create).with(expected_hash)
|
40
|
+
subject.notify
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'github integration' do
|
45
|
+
let(:error_id) { 1 }
|
46
|
+
|
47
|
+
it 'passes error id from request to github integration service' do
|
48
|
+
Crashbreak.configure.github_repo_name = 'user/repo'
|
49
|
+
|
50
|
+
allow_any_instance_of(Crashbreak::ExceptionsRepository).to receive(:create).and_return(error_id)
|
51
|
+
expect_any_instance_of(Crashbreak::GithubIntegrationService).to receive(:initialize).with(error_id)
|
52
|
+
|
53
|
+
subject.notify
|
54
|
+
end
|
55
|
+
end
|
22
56
|
end
|
23
57
|
|
24
58
|
context 'with additional serializers' do
|
@@ -2,6 +2,7 @@ describe Crashbreak::ExceptionsRepository do
|
|
2
2
|
subject { described_class.new }
|
3
3
|
|
4
4
|
let(:project_token) { 'example_project_token' }
|
5
|
+
let(:exception_id) { 1 }
|
5
6
|
|
6
7
|
before(:each) do
|
7
8
|
Crashbreak.configure.api_key = project_token
|
@@ -16,11 +17,20 @@ describe Crashbreak::ExceptionsRepository do
|
|
16
17
|
|
17
18
|
let!(:create_exception_request) do
|
18
19
|
stub_request(:post, "#{described_class::BASE_URL}/projects/#{project_token}/errors").
|
19
|
-
with(body: error_report_hash.to_json).to_return(status: 200)
|
20
|
+
with(body: error_report_hash.to_json).to_return(status: 200, body: { id: exception_id }.to_json)
|
21
|
+
end
|
22
|
+
|
23
|
+
let!(:resolve_exception_request) do
|
24
|
+
stub_request(:put, "#{described_class::BASE_URL}/projects/#{project_token}/errors/#{exception_id}").
|
25
|
+
with(body: { error_report: { status: :resolved } }.to_json)
|
20
26
|
end
|
21
27
|
|
22
28
|
it 'sends request to create exception report' do
|
23
|
-
subject.create error_report_hash
|
29
|
+
expect(subject.create error_report_hash).to eq exception_id
|
24
30
|
expect(create_exception_request).to have_been_made
|
25
31
|
end
|
32
|
+
|
33
|
+
it 'resolves error' do
|
34
|
+
subject.resolve exception_id
|
35
|
+
end
|
26
36
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'generator_spec'
|
2
|
+
require_relative '../../../lib/generators/crashbreak/test_generator'
|
3
|
+
|
4
|
+
describe Crashbreak::Generators::TestGenerator do
|
5
|
+
destination File.expand_path('../../../../tmp', __FILE__)
|
6
|
+
arguments %w(example_error_id)
|
7
|
+
|
8
|
+
before(:all) do
|
9
|
+
prepare_destination
|
10
|
+
run_generator
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'creates a test' do
|
14
|
+
assert_file 'spec/error_request_spec.rb', /example_error_id/
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
describe Crashbreak::GithubIntegrationService do
|
2
|
+
subject { described_class.new error_id }
|
3
|
+
|
4
|
+
let(:error_id) { 1 }
|
5
|
+
let(:branch_name) { 'refs/heads/crashbreak-error-1' }
|
6
|
+
let(:git_sha) { 'example_sha' }
|
7
|
+
let(:file_content) { 'example_file_content' }
|
8
|
+
|
9
|
+
let(:github_refs_url) { 'https://api.github.com/repos/user/repo/git/refs' }
|
10
|
+
let(:github_master_ref_url) { "#{github_refs_url}/heads/master" }
|
11
|
+
let(:github_create_content_url) { 'https://api.github.com/repos/user/repo/contents' }
|
12
|
+
let(:github_test_file_url) { 'https://api.github.com/repos/user/repo/contents/spec/crashbreak_error_spec.rb' }
|
13
|
+
let(:github_pull_requests_url) { 'https://api.github.com/repos/user/repo/pulls' }
|
14
|
+
|
15
|
+
before(:each) do
|
16
|
+
Crashbreak.configure.github_spec_file_path = 'spec/crashbreak_error_spec.rb'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'pushes test to github' do
|
20
|
+
stub_request(:get, github_master_ref_url).to_return(body: DeepStruct.wrap(object: { sha: git_sha } ))
|
21
|
+
|
22
|
+
stub_request(:post, github_refs_url).with(body: { ref: 'refs/heads/crashbreak-error-1', sha: git_sha}.to_json)
|
23
|
+
|
24
|
+
stub_request(:put, "#{github_create_content_url}/#{Crashbreak.configure.github_spec_file_path}").
|
25
|
+
with(body: { branch: branch_name, content: Base64.strict_encode64(file_content), message: 'Add test file for error 1' }.to_json)
|
26
|
+
|
27
|
+
allow(subject).to receive(:file_content).and_return(file_content)
|
28
|
+
|
29
|
+
subject.push_test
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'removes test from github' do
|
33
|
+
stub_request(:get, "#{github_test_file_url}?ref=heads/crashbreak-error-1").to_return(body: DeepStruct.wrap(sha: git_sha))
|
34
|
+
|
35
|
+
stub_request(:delete, github_test_file_url).
|
36
|
+
with(body: { branch: 'refs/heads/crashbreak-error-1', message: 'Remove test for error 1', sha: git_sha }.to_json)
|
37
|
+
|
38
|
+
subject.remove_test
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'creates pull request' do
|
42
|
+
stub_request(:post, github_pull_requests_url).
|
43
|
+
with(body: { base: 'master', head: 'crashbreak-error-1', title: 'Crashbreak - fix for error 1', body: '' }.to_json)
|
44
|
+
|
45
|
+
subject.create_pull_request
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
describe Crashbreak::RequestParser do
|
2
|
+
subject { described_class.new request_data }
|
3
|
+
|
4
|
+
context 'simple get without headers' do
|
5
|
+
let(:request_data) { Hash['REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/projects' ]}
|
6
|
+
|
7
|
+
it 'yields with request data' do
|
8
|
+
expect{|y| subject.request_data(&y) }.to yield_with_args(:get, '/projects', {}, {})
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'get with headers' do
|
13
|
+
let(:request_data) { Hash['REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/projects',
|
14
|
+
'HTTP_ACCEPT' => 'text/html', 'HTTP_CACHE_CONTROL' => 'max-age=0']}
|
15
|
+
|
16
|
+
it 'yields with request data' do
|
17
|
+
expect{|y| subject.request_data(&y) }.to yield_with_args(:get, '/projects', {}, { 'ACCEPT' => 'text/html', 'CACHE_CONTROL' => 'max-age=0' })
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'post with body' do
|
22
|
+
let(:request_data) { Hash['REQUEST_METHOD' => 'POST', 'PATH_INFO' => '/projects',
|
23
|
+
'HTTP_ACCEPT' => 'text/html', 'HTTP_CACHE_CONTROL' => 'max-age=0',
|
24
|
+
'action_dispatch.request.request_parameters' => request_parameters.to_json
|
25
|
+
]}
|
26
|
+
|
27
|
+
let(:request_parameters) { Hash[test_parameter: 'test_value', second_test_parameter: 'second_test_value'] }
|
28
|
+
|
29
|
+
it 'yields with request data' do
|
30
|
+
expect{|y| subject.request_data(&y) }.to yield_with_args(:post, '/projects', request_parameters, { 'ACCEPT' => 'text/html', 'CACHE_CONTROL' => 'max-age=0' })
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
describe StateRestorer do
|
2
|
+
subject { described_class.new error_id }
|
3
|
+
let(:error_id) { 1 }
|
4
|
+
let(:project_token) { 'example_project_token'}
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
Crashbreak.configure.api_key = project_token
|
8
|
+
|
9
|
+
stub_request(:get, "#{Crashbreak::DumpersDataRepository::BASE_URL}/projects/#{project_token}/errors/#{error_id}/dumpers_data").
|
10
|
+
to_return(status: 200, body: dumpers_data.to_json)
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:dumpers_data) { Hash['TestDumper' => 'test_data'] }
|
14
|
+
|
15
|
+
it 'returns a hash with all restorers data' do
|
16
|
+
expect_any_instance_of(TestRestorer).to receive(:initialize).with('test_data')
|
17
|
+
expect(subject.restore).to eq(test: 'restored_test_data')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class TestRestorer
|
22
|
+
def initialize(data)
|
23
|
+
@data = data
|
24
|
+
end
|
25
|
+
|
26
|
+
def restore
|
27
|
+
'restored_test_data'
|
28
|
+
end
|
29
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crashbreak
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michal Janeczek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: deepstruct
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: faraday
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +136,34 @@ dependencies:
|
|
122
136
|
- - ">="
|
123
137
|
- !ruby/object:Gem::Version
|
124
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: octokit
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: aws-sdk
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '2'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '2'
|
125
167
|
description: Maybe later... :)
|
126
168
|
email:
|
127
169
|
- michal.janeczek@ymail.com
|
@@ -138,8 +180,10 @@ files:
|
|
138
180
|
- Rakefile
|
139
181
|
- crashbreak.gemspec
|
140
182
|
- lib/crashbreak.rb
|
183
|
+
- lib/crashbreak/AWS.rb
|
141
184
|
- lib/crashbreak/config/configurable.rb
|
142
185
|
- lib/crashbreak/config/configurator.rb
|
186
|
+
- lib/crashbreak/dumpers_data_repository.rb
|
143
187
|
- lib/crashbreak/exception_catcher_middleware.rb
|
144
188
|
- lib/crashbreak/exception_notifier.rb
|
145
189
|
- lib/crashbreak/exceptions_repository.rb
|
@@ -149,16 +193,24 @@ files:
|
|
149
193
|
- lib/crashbreak/formatters/environment_variables_formatter.rb
|
150
194
|
- lib/crashbreak/formatters/hash_formatter.rb
|
151
195
|
- lib/crashbreak/formatters/summary_formatter.rb
|
196
|
+
- lib/crashbreak/github_integration_service.rb
|
197
|
+
- lib/crashbreak/request_parser.rb
|
152
198
|
- lib/crashbreak/version.rb
|
153
|
-
- lib/dumpers/
|
199
|
+
- lib/dumpers/database_dumper.rb
|
200
|
+
- lib/dumpers/request_dumper.rb
|
154
201
|
- lib/generators/crashbreak/install_generator.rb
|
155
202
|
- lib/generators/crashbreak/templates/crashbreak.rb
|
156
|
-
- lib/
|
203
|
+
- lib/generators/crashbreak/templates/test.rb
|
204
|
+
- lib/generators/crashbreak/test_generator.rb
|
205
|
+
- lib/restorers/database_restorer.rb
|
206
|
+
- lib/restorers/request_restorer.rb
|
207
|
+
- lib/restorers/state_restorer.rb
|
157
208
|
- lib/tasks/crashbreak.rake
|
158
|
-
- spec/dumpers/
|
209
|
+
- spec/dumpers/request_dumper_spec.rb
|
159
210
|
- spec/features/sending_error_report_spec.rb
|
160
211
|
- spec/lib/config/configurable_spec.rb
|
161
212
|
- spec/lib/config/configurator_spec.rb
|
213
|
+
- spec/lib/dumpers_data_repository_spec.rb
|
162
214
|
- spec/lib/exception_catcher_middleware_spec.rb
|
163
215
|
- spec/lib/exception_notifier_spec.rb
|
164
216
|
- spec/lib/exceptions_repository_spec.rb
|
@@ -167,7 +219,10 @@ files:
|
|
167
219
|
- spec/lib/formatters/group_formatter_spec.rb
|
168
220
|
- spec/lib/formatters/summary_formatter_spec.rb
|
169
221
|
- spec/lib/generators/install_generator_spec.rb
|
170
|
-
- spec/
|
222
|
+
- spec/lib/generators/test_generator_spec.rb
|
223
|
+
- spec/lib/github_integration_service_spec.rb
|
224
|
+
- spec/lib/request_sender_spec.rb
|
225
|
+
- spec/restorers/state_restorer_spec.rb
|
171
226
|
- spec/spec_helper.rb
|
172
227
|
- spec/support/rspec_config.rb
|
173
228
|
- spec/support/test_error.rb
|
@@ -197,10 +252,11 @@ signing_key:
|
|
197
252
|
specification_version: 4
|
198
253
|
summary: Take a break from crashes!
|
199
254
|
test_files:
|
200
|
-
- spec/dumpers/
|
255
|
+
- spec/dumpers/request_dumper_spec.rb
|
201
256
|
- spec/features/sending_error_report_spec.rb
|
202
257
|
- spec/lib/config/configurable_spec.rb
|
203
258
|
- spec/lib/config/configurator_spec.rb
|
259
|
+
- spec/lib/dumpers_data_repository_spec.rb
|
204
260
|
- spec/lib/exception_catcher_middleware_spec.rb
|
205
261
|
- spec/lib/exception_notifier_spec.rb
|
206
262
|
- spec/lib/exceptions_repository_spec.rb
|
@@ -209,7 +265,10 @@ test_files:
|
|
209
265
|
- spec/lib/formatters/group_formatter_spec.rb
|
210
266
|
- spec/lib/formatters/summary_formatter_spec.rb
|
211
267
|
- spec/lib/generators/install_generator_spec.rb
|
212
|
-
- spec/
|
268
|
+
- spec/lib/generators/test_generator_spec.rb
|
269
|
+
- spec/lib/github_integration_service_spec.rb
|
270
|
+
- spec/lib/request_sender_spec.rb
|
271
|
+
- spec/restorers/state_restorer_spec.rb
|
213
272
|
- spec/spec_helper.rb
|
214
273
|
- spec/support/rspec_config.rb
|
215
274
|
- spec/support/test_error.rb
|
@@ -1,12 +0,0 @@
|
|
1
|
-
describe Crashbreak::ProgramNameDumper do
|
2
|
-
subject { described_class.new }
|
3
|
-
let(:file_path) { subject.dump.path }
|
4
|
-
|
5
|
-
it 'file name should be relevant to class' do
|
6
|
-
expect(File.basename(file_path)).to eq 'program_name.dump'
|
7
|
-
end
|
8
|
-
|
9
|
-
it 'checks file content' do
|
10
|
-
expect(File.read(file_path)).to match $PROGRAM_NAME
|
11
|
-
end
|
12
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
describe Crashbreak::ProgramNameRestorer do
|
2
|
-
subject { described_class.new }
|
3
|
-
let(:program_name) { 'crashbreak' }
|
4
|
-
|
5
|
-
before(:each) { allow(File).to receive(:readlines).and_return([program_name]) }
|
6
|
-
|
7
|
-
it 'restore global variable $PROGRAM_NAME from file' do
|
8
|
-
subject.restore
|
9
|
-
expect($PROGRAM_NAME).to eq program_name
|
10
|
-
end
|
11
|
-
end
|