codeclimate-services 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/.travis.yml +13 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +121 -0
- data/Rakefile +11 -0
- data/bin/bundler +16 -0
- data/bin/coderay +16 -0
- data/bin/nokogiri +16 -0
- data/bin/pry +16 -0
- data/bin/rake +16 -0
- data/codeclimate-services.gemspec +29 -0
- data/config/cacert.pem +4026 -0
- data/config/load.rb +4 -0
- data/lib/axiom/types/password.rb +7 -0
- data/lib/cc/formatters/linked_formatter.rb +60 -0
- data/lib/cc/formatters/plain_formatter.rb +36 -0
- data/lib/cc/formatters/snapshot_formatter.rb +101 -0
- data/lib/cc/formatters/ticket_formatter.rb +27 -0
- data/lib/cc/helpers/coverage_helper.rb +25 -0
- data/lib/cc/helpers/issue_helper.rb +9 -0
- data/lib/cc/helpers/quality_helper.rb +44 -0
- data/lib/cc/helpers/vulnerability_helper.rb +31 -0
- data/lib/cc/presenters/github_pull_requests_presenter.rb +54 -0
- data/lib/cc/service/config.rb +4 -0
- data/lib/cc/service/formatter.rb +34 -0
- data/lib/cc/service/helper.rb +54 -0
- data/lib/cc/service/http.rb +87 -0
- data/lib/cc/service/invocation/invocation_chain.rb +15 -0
- data/lib/cc/service/invocation/with_error_handling.rb +45 -0
- data/lib/cc/service/invocation/with_metrics.rb +37 -0
- data/lib/cc/service/invocation/with_retries.rb +17 -0
- data/lib/cc/service/invocation/with_return_values.rb +18 -0
- data/lib/cc/service/invocation.rb +57 -0
- data/lib/cc/service/response_check.rb +42 -0
- data/lib/cc/service.rb +127 -0
- data/lib/cc/services/asana.rb +90 -0
- data/lib/cc/services/campfire.rb +55 -0
- data/lib/cc/services/flowdock.rb +61 -0
- data/lib/cc/services/github_issues.rb +80 -0
- data/lib/cc/services/github_pull_requests.rb +210 -0
- data/lib/cc/services/hipchat.rb +57 -0
- data/lib/cc/services/jira.rb +93 -0
- data/lib/cc/services/lighthouse.rb +79 -0
- data/lib/cc/services/pivotal_tracker.rb +78 -0
- data/lib/cc/services/slack.rb +124 -0
- data/lib/cc/services/version.rb +5 -0
- data/lib/cc/services.rb +9 -0
- data/pull_request_test.rb +47 -0
- data/service_test.rb +86 -0
- data/test/asana_test.rb +85 -0
- data/test/axiom/types/password_test.rb +22 -0
- data/test/campfire_test.rb +144 -0
- data/test/fixtures.rb +68 -0
- data/test/flowdock_test.rb +148 -0
- data/test/formatters/snapshot_formatter_test.rb +47 -0
- data/test/github_issues_test.rb +96 -0
- data/test/github_pull_requests_test.rb +293 -0
- data/test/helper.rb +50 -0
- data/test/hipchat_test.rb +130 -0
- data/test/invocation_error_handling_test.rb +51 -0
- data/test/invocation_return_values_test.rb +21 -0
- data/test/invocation_test.rb +167 -0
- data/test/jira_test.rb +80 -0
- data/test/lighthouse_test.rb +74 -0
- data/test/pivotal_tracker_test.rb +73 -0
- data/test/presenters/github_pull_requests_presenter_test.rb +49 -0
- data/test/service_test.rb +63 -0
- data/test/slack_test.rb +222 -0
- data/test/support/fake_logger.rb +11 -0
- data/test/with_metrics_test.rb +19 -0
- metadata +263 -0
data/config/load.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module CC
|
2
|
+
module Formatters
|
3
|
+
class LinkedFormatter < CC::Service::Formatter
|
4
|
+
def format_test
|
5
|
+
message = message_prefix
|
6
|
+
message << "This is a test of the #{service_title} service hook"
|
7
|
+
end
|
8
|
+
|
9
|
+
def format_coverage
|
10
|
+
message = message_prefix
|
11
|
+
message << "#{format_link(details_url, "Test coverage")}"
|
12
|
+
message << " has #{changed} to #{covered_percent}% (#{delta})"
|
13
|
+
|
14
|
+
if compare_url
|
15
|
+
message << " (#{format_link(compare_url, "Compare")})"
|
16
|
+
end
|
17
|
+
|
18
|
+
message
|
19
|
+
end
|
20
|
+
|
21
|
+
def format_quality
|
22
|
+
message = message_prefix
|
23
|
+
message << "#{format_link(details_url, constant_name)}"
|
24
|
+
message << " has #{changed} from #{previous_rating} to #{rating}"
|
25
|
+
|
26
|
+
if compare_url
|
27
|
+
message << " (#{format_link(compare_url, "Compare")})"
|
28
|
+
end
|
29
|
+
|
30
|
+
message
|
31
|
+
end
|
32
|
+
|
33
|
+
def format_vulnerability
|
34
|
+
message = message_prefix
|
35
|
+
|
36
|
+
if multiple?
|
37
|
+
message << "#{vulnerabilities.size} new"
|
38
|
+
message << " #{format_link(details_url, warning_type)}"
|
39
|
+
message << " issues found"
|
40
|
+
else
|
41
|
+
message << "New #{format_link(details_url, warning_type)}"
|
42
|
+
message << " issue found"
|
43
|
+
message << location_info
|
44
|
+
end
|
45
|
+
|
46
|
+
message
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def format_link(url, text)
|
52
|
+
case options[:link_style]
|
53
|
+
when :html then "<a href=\"#{url}\">#{text}</a>"
|
54
|
+
when :wiki then "<#{url}|#{text}>"
|
55
|
+
else text
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module CC
|
2
|
+
module Formatters
|
3
|
+
class PlainFormatter < CC::Service::Formatter
|
4
|
+
def format_test
|
5
|
+
message = message_prefix
|
6
|
+
message << "This is a test of the #{service_title} service hook"
|
7
|
+
end
|
8
|
+
|
9
|
+
def format_coverage
|
10
|
+
message = message_prefix
|
11
|
+
message << "#{emoji} Test coverage has #{changed}"
|
12
|
+
message << " to #{covered_percent}% (#{delta})."
|
13
|
+
message << " (#{details_url})"
|
14
|
+
end
|
15
|
+
|
16
|
+
def format_quality
|
17
|
+
message = message_prefix
|
18
|
+
message << "#{emoji} #{constant_name} has #{changed}"
|
19
|
+
message << " from #{previous_rating} to #{rating}."
|
20
|
+
message << " (#{details_url})"
|
21
|
+
end
|
22
|
+
|
23
|
+
def format_vulnerability
|
24
|
+
message = message_prefix
|
25
|
+
|
26
|
+
if multiple?
|
27
|
+
message << "#{vulnerabilities.size} new #{warning_type} issues found"
|
28
|
+
else
|
29
|
+
message << "New #{warning_type} issue found" << location_info
|
30
|
+
end
|
31
|
+
|
32
|
+
message << ". Details: #{details_url}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module CC::Formatters
|
2
|
+
module SnapshotFormatter
|
3
|
+
# Simple Comparator for rating letters.
|
4
|
+
class Rating
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
def initialize(letter)
|
8
|
+
@letter = letter
|
9
|
+
end
|
10
|
+
|
11
|
+
def <=>(other)
|
12
|
+
other.to_s <=> to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def hash
|
16
|
+
@letter.hash
|
17
|
+
end
|
18
|
+
|
19
|
+
def eql?(other)
|
20
|
+
to_s == other.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
"<Rating:#{to_s}>"
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
@letter.to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
C = Rating.new("C")
|
33
|
+
D = Rating.new("D")
|
34
|
+
|
35
|
+
# SnapshotFormatter::Base takes the quality information from the payload and divides it
|
36
|
+
# between alerts and improvements.
|
37
|
+
#
|
38
|
+
# The information in the payload must be a comparison in time between two quality reports, aka snapshot.
|
39
|
+
# This information is in the payload when the service receive a `receive_snapshot` and also
|
40
|
+
# when it receives a `receive_test`. In this latest case, the comparison is between today and seven days ago.
|
41
|
+
class Base
|
42
|
+
attr_reader :alert_constants_payload, :improved_constants_payload, :details_url, :compare_url
|
43
|
+
|
44
|
+
def initialize(payload)
|
45
|
+
new_constants = Array(payload["new_constants"])
|
46
|
+
changed_constants = Array(payload["changed_constants"])
|
47
|
+
|
48
|
+
alert_constants = new_constants.select(&new_constants_selector)
|
49
|
+
alert_constants += changed_constants.select(&decreased_constants_selector)
|
50
|
+
|
51
|
+
improved_constants = changed_constants.select(&improved_constants_selector)
|
52
|
+
|
53
|
+
data = {
|
54
|
+
"from" => { "commit_sha" => payload["previous_commit_sha"] },
|
55
|
+
"to" => { "commit_sha" => payload["commit_sha"] }
|
56
|
+
}
|
57
|
+
|
58
|
+
@alert_constants_payload = data.merge("constants" => alert_constants) if alert_constants.any?
|
59
|
+
@improved_constants_payload = data.merge("constants" => improved_constants) if improved_constants.any?
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def new_constants_selector
|
65
|
+
Proc.new { |constant| to_rating(constant) < C }
|
66
|
+
end
|
67
|
+
|
68
|
+
def decreased_constants_selector
|
69
|
+
Proc.new { |constant| from_rating(constant) > D && to_rating(constant) < C }
|
70
|
+
end
|
71
|
+
|
72
|
+
def improved_constants_selector
|
73
|
+
Proc.new { |constant| from_rating(constant) < C && to_rating(constant) > from_rating(constant) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_rating(constant)
|
77
|
+
Rating.new(constant["to"]["rating"])
|
78
|
+
end
|
79
|
+
|
80
|
+
def from_rating(constant)
|
81
|
+
Rating.new(constant["from"]["rating"])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Override the base snapshot formatter for be more lax grouping information.
|
86
|
+
# This is useful to show more information for testing the service.
|
87
|
+
class Sample < Base
|
88
|
+
def new_constants_selector
|
89
|
+
Proc.new { |_| true }
|
90
|
+
end
|
91
|
+
|
92
|
+
def decreased_constants_selector
|
93
|
+
Proc.new { |constant| to_rating(constant) < from_rating(constant) }
|
94
|
+
end
|
95
|
+
|
96
|
+
def improved_constants_selector
|
97
|
+
Proc.new { |constant| to_rating(constant) > from_rating(constant) }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module CC
|
2
|
+
module Formatters
|
3
|
+
class TicketFormatter < CC::Service::Formatter
|
4
|
+
|
5
|
+
def format_vulnerability_title
|
6
|
+
if multiple?
|
7
|
+
"#{vulnerabilities.size} new #{warning_type} issues found"
|
8
|
+
else
|
9
|
+
"New #{warning_type} issue found" << location_info
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def format_vulnerability_body
|
14
|
+
if multiple?
|
15
|
+
"#{vulnerabilities.size} new #{warning_type} issues were found by Code Climate"
|
16
|
+
else
|
17
|
+
message = "A #{warning_type} vulnerability was found by Code Climate"
|
18
|
+
message << location_info
|
19
|
+
end
|
20
|
+
|
21
|
+
message << ".\n\n"
|
22
|
+
message << details_url
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CC::Service::CoverageHelper
|
2
|
+
def improved?
|
3
|
+
covered_percent_delta > 0
|
4
|
+
end
|
5
|
+
|
6
|
+
def delta
|
7
|
+
if improved?
|
8
|
+
"+#{covered_percent_delta.round(1)}%"
|
9
|
+
else
|
10
|
+
"#{covered_percent_delta.round(1)}%"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def covered_percent
|
15
|
+
payload.fetch("covered_percent", 0).round(1)
|
16
|
+
end
|
17
|
+
|
18
|
+
def previous_covered_percent
|
19
|
+
payload.fetch("previous_covered_percent", 0).round(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
def covered_percent_delta
|
23
|
+
payload.fetch("covered_percent_delta", 0) # pre-rounded
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module CC::Service::QualityHelper
|
2
|
+
def improved?
|
3
|
+
remediation_cost < previous_remediation_cost
|
4
|
+
end
|
5
|
+
|
6
|
+
def constant_name
|
7
|
+
payload["constant_name"]
|
8
|
+
end
|
9
|
+
|
10
|
+
def rating
|
11
|
+
with_article(payload["rating"])
|
12
|
+
end
|
13
|
+
|
14
|
+
def previous_rating
|
15
|
+
with_article(payload["previous_rating"])
|
16
|
+
end
|
17
|
+
|
18
|
+
def remediation_cost
|
19
|
+
payload.fetch("remediation_cost", 0)
|
20
|
+
end
|
21
|
+
|
22
|
+
def previous_remediation_cost
|
23
|
+
payload.fetch("previous_remediation_cost", 0)
|
24
|
+
end
|
25
|
+
|
26
|
+
def with_article(letter, bold = false)
|
27
|
+
letter ||= '?'
|
28
|
+
|
29
|
+
text = bold ? "*#{letter}*" : letter
|
30
|
+
if %w( A F ).include?(letter.to_s)
|
31
|
+
"an #{text}"
|
32
|
+
else
|
33
|
+
"a #{text}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def constant_basename(name)
|
38
|
+
if name.include?(".")
|
39
|
+
File.basename(name)
|
40
|
+
else
|
41
|
+
name
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module CC::Service::VulnerabilityHelper
|
2
|
+
|
3
|
+
def vulnerability
|
4
|
+
vulnerabilities.first || {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def vulnerabilities
|
8
|
+
payload.fetch("vulnerabilities", [])
|
9
|
+
end
|
10
|
+
|
11
|
+
def multiple?
|
12
|
+
vulnerabilities.size > 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def location_info
|
16
|
+
if vulnerability["location"]
|
17
|
+
" in #{vulnerability["location"]}"
|
18
|
+
else
|
19
|
+
""
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def warning_type
|
24
|
+
if multiple?
|
25
|
+
payload["warning_type"]
|
26
|
+
else
|
27
|
+
vulnerability["warning_type"]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module CC
|
2
|
+
class Service
|
3
|
+
class GitHubPullRequestsPresenter
|
4
|
+
include ActiveSupport::NumberHelper
|
5
|
+
|
6
|
+
def initialize(payload)
|
7
|
+
issue_comparison_counts = payload["issue_comparison_counts"]
|
8
|
+
|
9
|
+
if issue_comparison_counts
|
10
|
+
@fixed_count = issue_comparison_counts["fixed"]
|
11
|
+
@new_count = issue_comparison_counts["new"]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def success_message
|
16
|
+
if both_issue_counts_zero?
|
17
|
+
"Code Climate didn't find any new or fixed issues."
|
18
|
+
else
|
19
|
+
"Code Climate found #{formatted_issue_counts}."
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def both_issue_counts_zero?
|
26
|
+
issue_counts.all?(&:zero?)
|
27
|
+
end
|
28
|
+
|
29
|
+
def formatted_fixed_issues
|
30
|
+
if @fixed_count > 0
|
31
|
+
"#{number_to_delimited(@fixed_count)} fixed #{"issue".pluralize(@fixed_count)}"
|
32
|
+
else
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def formatted_new_issues
|
38
|
+
if @new_count > 0
|
39
|
+
"#{number_to_delimited(@new_count)} new #{"issue".pluralize(@new_count)}"
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def formatted_issue_counts
|
46
|
+
[formatted_new_issues, formatted_fixed_issues].compact.to_sentence
|
47
|
+
end
|
48
|
+
|
49
|
+
def issue_counts
|
50
|
+
[@new_count, @fixed_count]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
class CC::Service::Formatter < SimpleDelegator
|
4
|
+
attr_reader :options
|
5
|
+
|
6
|
+
def initialize(service, options = {})
|
7
|
+
super(service)
|
8
|
+
|
9
|
+
@options = {
|
10
|
+
prefix: "[Code Climate]",
|
11
|
+
prefix_with_repo: true
|
12
|
+
}.merge(options)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def service_title
|
18
|
+
__getobj__.class.title
|
19
|
+
end
|
20
|
+
|
21
|
+
def message_prefix
|
22
|
+
prefix = options.fetch(:prefix, "").to_s
|
23
|
+
|
24
|
+
if options[:prefix_with_repo]
|
25
|
+
prefix << "[#{repo_name}]"
|
26
|
+
end
|
27
|
+
|
28
|
+
if !prefix.empty?
|
29
|
+
prefix << " "
|
30
|
+
end
|
31
|
+
|
32
|
+
prefix
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module CC::Service::Helper
|
2
|
+
GREEN_HEX = "#38ae6f"
|
3
|
+
RED_HEX = "#ed2f00"
|
4
|
+
|
5
|
+
def repo_name
|
6
|
+
payload["repo_name"]
|
7
|
+
end
|
8
|
+
|
9
|
+
def details_url
|
10
|
+
payload["details_url"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def compare_url
|
14
|
+
payload["compare_url"]
|
15
|
+
end
|
16
|
+
|
17
|
+
def emoji
|
18
|
+
if improved?
|
19
|
+
":sunny:"
|
20
|
+
else
|
21
|
+
":umbrella:"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def color
|
26
|
+
if improved?
|
27
|
+
"green"
|
28
|
+
else
|
29
|
+
"red"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def hex_color
|
34
|
+
if improved?
|
35
|
+
GREEN_HEX
|
36
|
+
else
|
37
|
+
RED_HEX
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def changed
|
42
|
+
if improved?
|
43
|
+
"improved"
|
44
|
+
else
|
45
|
+
"declined"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def improved?
|
50
|
+
raise NotImplementedError,
|
51
|
+
"Event-specific helpers must define #{__method__}"
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
require "cc/service/response_check"
|
3
|
+
|
4
|
+
module CC::Service::HTTP
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def default_http_options
|
9
|
+
@@default_http_options ||= {
|
10
|
+
adapter: :net_http,
|
11
|
+
request: { timeout: 10, open_timeout: 5 },
|
12
|
+
ssl: { verify_depth: 5 },
|
13
|
+
headers: {}
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def service_get(url = nil, body = nil, headers = nil, &block)
|
19
|
+
raw_get(url, body, headers, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def service_post(url, body = nil, headers = nil, &block)
|
23
|
+
block ||= lambda{|*args| Hash.new }
|
24
|
+
response = raw_post(url, body, headers)
|
25
|
+
{
|
26
|
+
ok: response.success?,
|
27
|
+
params: body.as_json,
|
28
|
+
endpoint_url: url,
|
29
|
+
status: response.status,
|
30
|
+
message: "Success"
|
31
|
+
}.merge(block.call(response))
|
32
|
+
end
|
33
|
+
|
34
|
+
def raw_get(url = nil, params = nil, headers = nil)
|
35
|
+
http.get do |req|
|
36
|
+
req.url(url) if url
|
37
|
+
req.params.update(params) if params
|
38
|
+
req.headers.update(headers) if headers
|
39
|
+
yield req if block_given?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def raw_post(url = nil, body = nil, headers = nil)
|
44
|
+
block = Proc.new if block_given?
|
45
|
+
http_method :post, url, body, headers, &block
|
46
|
+
end
|
47
|
+
|
48
|
+
def http_method(method, url = nil, body = nil, headers = nil)
|
49
|
+
block = Proc.new if block_given?
|
50
|
+
|
51
|
+
http.send(method) do |req|
|
52
|
+
req.url(url) if url
|
53
|
+
req.headers.update(headers) if headers
|
54
|
+
req.body = body if body
|
55
|
+
block.call req if block
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def http(options = {})
|
60
|
+
@http ||= begin
|
61
|
+
config = self.class.default_http_options
|
62
|
+
config.each do |key, sub_options|
|
63
|
+
next if key == :adapter
|
64
|
+
sub_hash = options[key] ||= {}
|
65
|
+
sub_options.each do |sub_key, sub_value|
|
66
|
+
sub_hash[sub_key] ||= sub_value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
options[:ssl][:ca_file] ||= ca_file
|
70
|
+
|
71
|
+
Faraday.new(options) do |b|
|
72
|
+
b.use(CC::Service::ResponseCheck)
|
73
|
+
b.request(:url_encoded)
|
74
|
+
b.adapter(*Array(options[:adapter] || config[:adapter]))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Gets the path to the SSL Certificate Authority certs. These were taken
|
80
|
+
# from: http://curl.haxx.se/ca/cacert.pem
|
81
|
+
#
|
82
|
+
# Returns a String path.
|
83
|
+
def ca_file
|
84
|
+
@ca_file ||= ENV.fetch("CODECLIMATE_CA_FILE", File.expand_path('../../../../config/cacert.pem', __FILE__))
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class CC::Service::Invocation
|
2
|
+
class WithErrorHandling
|
3
|
+
def initialize(invocation, logger, prefix = nil)
|
4
|
+
@invocation = invocation
|
5
|
+
@logger = logger
|
6
|
+
@prefix = prefix
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
@invocation.call
|
11
|
+
rescue CC::Service::HTTPError => e
|
12
|
+
@logger.error(error_message(e))
|
13
|
+
{
|
14
|
+
ok: false,
|
15
|
+
params: e.params,
|
16
|
+
status: e.status,
|
17
|
+
endpoint_url: e.endpoint_url,
|
18
|
+
message: e.user_message || e.message,
|
19
|
+
log_message: error_message(e)
|
20
|
+
}
|
21
|
+
rescue => e
|
22
|
+
@logger.error(error_message(e))
|
23
|
+
{
|
24
|
+
ok: false,
|
25
|
+
message: e.message,
|
26
|
+
log_message: error_message(e)
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def error_message(e)
|
33
|
+
if e.respond_to?(:response_body)
|
34
|
+
response_body = ". Response: <#{e.response_body.inspect}>"
|
35
|
+
else
|
36
|
+
response_body = ""
|
37
|
+
end
|
38
|
+
|
39
|
+
message = "Exception invoking service:"
|
40
|
+
message << " [#{@prefix}]" if @prefix
|
41
|
+
message << " (#{e.class}) #{e.message}"
|
42
|
+
message << response_body
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class CC::Service::Invocation
|
2
|
+
class WithMetrics
|
3
|
+
def initialize(invocation, statsd, prefix = nil)
|
4
|
+
@invocation = invocation
|
5
|
+
@statsd = statsd
|
6
|
+
@prefix = prefix
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
start_time = Time.now
|
11
|
+
|
12
|
+
result = @invocation.call
|
13
|
+
@statsd.increment(success_key)
|
14
|
+
|
15
|
+
result
|
16
|
+
rescue => ex
|
17
|
+
@statsd.increment(error_key(ex))
|
18
|
+
raise ex
|
19
|
+
ensure
|
20
|
+
duration = ((Time.now - start_time) * 1_000).round
|
21
|
+
@statsd.timing(timing_key, duration)
|
22
|
+
end
|
23
|
+
|
24
|
+
def success_key
|
25
|
+
["services.invocations", @prefix].compact.join('.')
|
26
|
+
end
|
27
|
+
|
28
|
+
def timing_key
|
29
|
+
["services.timing", @prefix].compact.join('.')
|
30
|
+
end
|
31
|
+
|
32
|
+
def error_key(ex)
|
33
|
+
error_string = ex.class.name.underscore.gsub("/", "-")
|
34
|
+
["services.errors", @prefix, error_string].compact.join('.')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|