codeclimate-services 0.3.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 +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
|