controls 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/controls.gemspec +2 -2
- data/lib/controls.rb +2 -0
- data/lib/controls/client.rb +44 -9
- data/lib/controls/default.rb +0 -1
- data/lib/controls/ext/dish/plate.rb +46 -0
- data/lib/controls/objects.rb +11 -0
- data/lib/controls/objects/assessment.rb +9 -0
- data/lib/controls/objects/asset.rb +36 -0
- data/lib/controls/objects/configuration.rb +9 -0
- data/lib/controls/objects/configuration_finding.rb +9 -0
- data/lib/controls/objects/guidance.rb +17 -0
- data/lib/controls/objects/security_control.rb +7 -0
- data/lib/controls/objects/security_control_coverage.rb +9 -0
- data/lib/controls/objects/security_control_finding.rb +12 -0
- data/lib/controls/objects/threat.rb +9 -0
- data/lib/controls/objects/threat_vector.rb +9 -0
- data/lib/controls/objects/trend.rb +9 -0
- data/lib/controls/response.rb +21 -65
- data/lib/controls/version.rb +1 -1
- metadata +17 -6
- data/lib/controls/error.rb +0 -132
- data/lib/controls/response/raise_error.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f428c9a710108971da0e6995f4f0ca0032caeed4
|
4
|
+
data.tar.gz: e727ddbd5d77bc6afdfd84ef51a81bc57bc8dfc4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1455b974d58516093e986277014a653b157ab5f1a0da5d22742b7554a5671074f001ae458ab14cff15cf18ffdc7a5b7c1742c713aea4eb0a41b21bae8466d111
|
7
|
+
data.tar.gz: fd418654c0932982591cd8d717354796194dd376a7214397fb553f348b1d742f5cedd64cddc18237bd6e2eb3dbf280a4c890a05fb36d2901f1c9d18595efb99d
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# controlsinsight client gem
|
2
|
-
The **controls**insight (controls) gem interfaces with [Rapid7's **controls**insight API](http://rapid7.
|
2
|
+
The **controls**insight (controls) gem interfaces with [Rapid7's **controls**insight API](http://rapid7.github.io/controlsinsight.rb).
|
3
3
|
|
4
4
|
## Installation
|
5
5
|
Add this line to your application's Gemfile:
|
@@ -15,7 +15,7 @@ Or install it yourself as:
|
|
15
15
|
$ gem install controls
|
16
16
|
|
17
17
|
## Documentation
|
18
|
-
* [
|
18
|
+
* [API documentation](http://rapid7.github.io/controlsinsight.rb)
|
19
19
|
* [YARD documentation for the Ruby client](http://www.rubydoc.info/github/rapid7/controlsinsight.rb)
|
20
20
|
|
21
21
|
## Basic Resources
|
@@ -101,4 +101,4 @@ Controls.configuration_trends('configuration-name-here')
|
|
101
101
|
```
|
102
102
|
|
103
103
|
## License
|
104
|
-
This project was created by [Erran Carey (@
|
104
|
+
This project was created by [Erran Carey (@erran)](http://erran.github.io) and licensed under [the MIT License](LICENSE.md).
|
data/controls.gemspec
CHANGED
@@ -18,9 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = %w[lib]
|
20
20
|
|
21
|
-
spec.add_dependency '
|
21
|
+
spec.add_dependency 'dish'
|
22
22
|
spec.add_dependency 'faraday', '< 0.9'
|
23
|
-
spec.add_dependency '
|
23
|
+
spec.add_dependency 'rack'
|
24
24
|
|
25
25
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
26
26
|
spec.add_development_dependency 'netrc'
|
data/lib/controls.rb
CHANGED
data/lib/controls/client.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
require 'faraday'
|
2
1
|
require 'json'
|
2
|
+
require 'faraday'
|
3
3
|
require 'nokogiri'
|
4
|
+
require 'rack/utils'
|
4
5
|
require 'controls/authentication'
|
5
6
|
require 'controls/configurable'
|
6
7
|
require 'controls/client/assessments'
|
@@ -88,8 +89,16 @@ module Controls
|
|
88
89
|
headers = connection_options[:headers].merge(headers)
|
89
90
|
url = URI.escape(File.join(api_endpoint, path))
|
90
91
|
resp = middleware.get(url, params, headers)
|
92
|
+
@_last_request = {
|
93
|
+
response: resp,
|
94
|
+
path: path
|
95
|
+
}
|
96
|
+
|
97
|
+
if !resp.headers['content-type'].eql?("application/json;charset=UTF-8")
|
98
|
+
fail exception('Invalid content-type error')
|
99
|
+
end
|
91
100
|
|
92
|
-
Response.parse(resp.body)
|
101
|
+
Response.parse(resp.body, path)
|
93
102
|
rescue Faraday::Error::ConnectionFailed => e
|
94
103
|
if e.message =~ /^SSL_connect/
|
95
104
|
warn(*SSL_WARNING)
|
@@ -105,8 +114,16 @@ module Controls
|
|
105
114
|
headers = connection_options[:headers].merge(headers)
|
106
115
|
url = URI.escape(File.join(api_endpoint, path))
|
107
116
|
resp = middleware.put(url, body, headers, &block)
|
117
|
+
@_last_request = {
|
118
|
+
response: resp,
|
119
|
+
path: path
|
120
|
+
}
|
108
121
|
|
109
|
-
|
122
|
+
if !resp.headers['content-type'].eql?("application/json;charset=UTF-8")
|
123
|
+
fail exception('Invalid content-type error')
|
124
|
+
end
|
125
|
+
|
126
|
+
Response.parse(resp.body, path)
|
110
127
|
rescue Faraday::Error::ConnectionFailed => e
|
111
128
|
if e.message =~ /^SSL_connect/
|
112
129
|
warn(*SSL_WARNING)
|
@@ -134,11 +151,7 @@ module Controls
|
|
134
151
|
# @param [String] version the API version to collect documentation from
|
135
152
|
def references(version = '1.0')
|
136
153
|
version = '1.0' unless version =~ /\d.\d/
|
137
|
-
|
138
|
-
web_get "/api/#{version}"
|
139
|
-
|
140
|
-
# [review] - Use Response#generate_ruby
|
141
|
-
@references = Hash[Response.parse(resp.body).sort]
|
154
|
+
web_get "/api/#{version}"
|
142
155
|
rescue Faraday::Error::ConnectionFailed => e
|
143
156
|
if e.message =~ /^SSL_connect/
|
144
157
|
warn(*SSL_WARNING)
|
@@ -176,8 +189,16 @@ module Controls
|
|
176
189
|
headers = connection_options[:headers].merge(headers)
|
177
190
|
url = URI.escape(File.join(web_endpoint, path))
|
178
191
|
resp = middleware.get(url, params, headers)
|
192
|
+
@_last_request = {
|
193
|
+
response: resp,
|
194
|
+
path: path
|
195
|
+
}
|
179
196
|
|
180
|
-
|
197
|
+
if !resp.headers['content-type'].eql?("application/json;charset=UTF-8")
|
198
|
+
fail exception('Invalid content-type error')
|
199
|
+
end
|
200
|
+
|
201
|
+
JSON.parse(resp.body)
|
181
202
|
rescue Faraday::Error::ConnectionFailed => e
|
182
203
|
if e.message =~ /^SSL_connect/
|
183
204
|
warn(*SSL_WARNING)
|
@@ -185,5 +206,19 @@ module Controls
|
|
185
206
|
raise e
|
186
207
|
end
|
187
208
|
end
|
209
|
+
|
210
|
+
def exception(message = "HTTP Error")
|
211
|
+
last_request = _last_request
|
212
|
+
if last_request
|
213
|
+
message << ": #{last_request[:response].status} #{Rack::Utils::HTTP_STATUS_CODES[last_request[:response].status]} #{last_request[:path]}"
|
214
|
+
else
|
215
|
+
message = 'Unknown error'
|
216
|
+
end
|
217
|
+
|
218
|
+
Controls::Error.new(message)
|
219
|
+
end
|
220
|
+
|
221
|
+
private
|
222
|
+
attr_reader :_last_request
|
188
223
|
end
|
189
224
|
end
|
data/lib/controls/default.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
module Dish
|
2
|
+
class Plate
|
3
|
+
def method_missing(method, *args, &block)
|
4
|
+
method = method.to_s
|
5
|
+
camel_case_key = method.split('_').map(&:capitalize).join
|
6
|
+
camel_case_key[0] = camel_case_key[0].downcase
|
7
|
+
|
8
|
+
if method.end_with?('?')
|
9
|
+
key = camel_case_key[0..-2]
|
10
|
+
_check_for_presence(key)
|
11
|
+
else
|
12
|
+
value = _get_value(camel_case_key)
|
13
|
+
if value.nil?
|
14
|
+
super(method.to_sym, *args, &block)
|
15
|
+
else
|
16
|
+
value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def methods()
|
22
|
+
valid_keys = as_hash.keys.map do |key|
|
23
|
+
key.to_s.gsub(/([^A-Z])([A-Z]+)/, '\1_\2').downcase.to_sym
|
24
|
+
end
|
25
|
+
|
26
|
+
valid_keys + super
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
hash = as_hash
|
31
|
+
keys_to_snake_case = hash.keys.map { |key|
|
32
|
+
[key, key.to_s.gsub(/([^A-Z])([A-Z]+)/, '\1_\2').downcase]
|
33
|
+
}.to_h
|
34
|
+
|
35
|
+
vars = hash.map do |key, value|
|
36
|
+
"#{keys_to_snake_case[key]}: #{_get_value(key)}"
|
37
|
+
end
|
38
|
+
|
39
|
+
"#<#{self.class}: #{vars.join(', ')}>"
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_json(*args)
|
43
|
+
as_hash.to_json(*args)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'dish'
|
2
|
+
require 'controls/ext/dish/plate'
|
3
|
+
require 'controls/objects/assessment'
|
4
|
+
require 'controls/objects/asset'
|
5
|
+
require 'controls/objects/configuration'
|
6
|
+
require 'controls/objects/guidance'
|
7
|
+
require 'controls/objects/security_control'
|
8
|
+
require 'controls/objects/security_control_coverage'
|
9
|
+
require 'controls/objects/threat'
|
10
|
+
require 'controls/objects/threat_vector'
|
11
|
+
require 'controls/objects/trend'
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'controls/objects/security_control_finding'
|
2
|
+
|
3
|
+
module Controls
|
4
|
+
class Asset < Dish::Plate
|
5
|
+
coerce :discoveredAt, ->(value) { Time.at(value / 1000) if value }
|
6
|
+
coerce :securityControlFindings, Controls::SecurityControlFinding
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
%(#{host_name} (#{ipaddress}) - #{operating_system})
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class AssetCollection < Dish::Plate
|
14
|
+
coerce :resources, Asset
|
15
|
+
|
16
|
+
def map(*args, &block)
|
17
|
+
resources.map(*args, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def first
|
21
|
+
resources.first
|
22
|
+
end
|
23
|
+
|
24
|
+
def last
|
25
|
+
resources.last
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](index)
|
29
|
+
resources[index]
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
resources.sort_by(&:ipaddress).map(&:to_s).join("\n")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Controls
|
2
|
+
Guidance = Class.new(Dish::Plate)
|
3
|
+
Guidance::Reference = Class.new(Dish::Plate)
|
4
|
+
Guidance::Section = Class.new(Dish::Plate)
|
5
|
+
|
6
|
+
class Guidance
|
7
|
+
coerce :assessmentTimestamp, ->(value) { Time.at(value / 1000) if value }
|
8
|
+
coerce :references, Reference
|
9
|
+
coerce :sections, Section
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
title
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
PrioritizedGuidance = Class.new(Guidance)
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'controls/objects/configuration_finding'
|
2
|
+
|
3
|
+
module Controls
|
4
|
+
class SecurityControlFinding < Dish::Plate
|
5
|
+
coerce :assessmentTimestamp, ->(value) { Time.at(value / 1000) if value }
|
6
|
+
coerce :configurationFindings, Controls::ConfigurationFinding
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
"#{security_control_name}: #{configuration_findings.map { |finding| "\n #{finding}" }.join}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/controls/response.rb
CHANGED
@@ -1,76 +1,32 @@
|
|
1
|
-
require '
|
2
|
-
require 'active_support/core_ext/hash/keys'
|
3
|
-
require 'controls/response/raise_error'
|
1
|
+
require 'controls/objects'
|
4
2
|
|
5
3
|
module Controls
|
6
|
-
# A
|
4
|
+
# A module to encapsulate middleware customization
|
7
5
|
module Response
|
8
|
-
|
9
|
-
|
10
|
-
# @return [Array,Hash] the response after being parsed into an Array or
|
11
|
-
# Hash
|
12
|
-
def self.parse(response)
|
13
|
-
response = JSON.parse(response)
|
14
|
-
end
|
6
|
+
def self.parse(obj, path = nil)
|
7
|
+
hash_or_array = JSON.parse(obj)
|
15
8
|
|
16
|
-
|
17
|
-
|
18
|
-
def self.generate_ruby(response)
|
19
|
-
if response.is_a? String
|
20
|
-
response = JSON.parse(response)
|
9
|
+
if hash_or_array.is_a?(Hash) && hash_or_array.key?('message') && hash_or_array.key?('documentationUrl')
|
10
|
+
type = Controls::Error
|
21
11
|
end
|
22
12
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
if subhash.has_key? 'configuration_findings'
|
39
|
-
subhash['configuration_findings'].each do |subhash_two|
|
40
|
-
subhash_two.deep_transform_keys! { |key| key.underscore }
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
13
|
+
type ||=
|
14
|
+
case path
|
15
|
+
when %r(^(?:/\d.\d)?/coverage/security_controls)
|
16
|
+
Controls::SecurityControlCoverage
|
17
|
+
when /(configuration|event|guidance|prioritized_guidance|security_control|threat_vector|trend)s?$/
|
18
|
+
Controls.const_get(Regexp.last_match[1].split('_').map(&:capitalize).join)
|
19
|
+
when %r(^(?:/\d.\d)?\/(assessment|configuration|threat|threat_vector)s)
|
20
|
+
Controls.const_get(Regexp.last_match[1].split('_').map(&:capitalize).join)
|
21
|
+
when /((?:applicable|miconfigured|uncovered|undefended)?_?asset)s$/
|
22
|
+
Controls.const_get('AssetCollection')
|
23
|
+
when %r(^/((?:applicable|miconfigured|uncovered|undefended)?_?asset)s/)
|
24
|
+
Controls.const_get('Asset')
|
25
|
+
else
|
26
|
+
Dish::Plate
|
46
27
|
end
|
47
|
-
elsif response.is_a? Array
|
48
|
-
response.each do |element|
|
49
|
-
if element.is_a? Hash
|
50
|
-
element.deep_transform_keys! { |key| key.underscore }
|
51
|
-
|
52
|
-
if element.has_key? 'resources'
|
53
|
-
element['resources'].each do |hash|
|
54
|
-
hash.deep_transform_keys! { |key| key.underscore }
|
55
|
-
|
56
|
-
if hash.has_key? 'security_control_findings'
|
57
|
-
hash['security_control_findings'].each do |subhash|
|
58
|
-
subhash.deep_transform_keys! { |key| key.underscore }
|
59
|
-
|
60
|
-
if subhash.has_key? 'configuration_findings'
|
61
|
-
subhash['configuration_findings'].each do |subhash_two|
|
62
|
-
subhash_two.deep_transform_keys! { |key| key.underscore }
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
28
|
|
73
|
-
|
29
|
+
Dish(hash_or_array, type)
|
74
30
|
end
|
75
31
|
end
|
76
32
|
end
|
data/lib/controls/version.rb
CHANGED
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: controls
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Erran Carey
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: dish
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -39,7 +39,7 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.9'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rack
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -175,9 +175,20 @@ files:
|
|
175
175
|
- lib/controls/client/trends.rb
|
176
176
|
- lib/controls/configurable.rb
|
177
177
|
- lib/controls/default.rb
|
178
|
-
- lib/controls/
|
178
|
+
- lib/controls/ext/dish/plate.rb
|
179
|
+
- lib/controls/objects.rb
|
180
|
+
- lib/controls/objects/assessment.rb
|
181
|
+
- lib/controls/objects/asset.rb
|
182
|
+
- lib/controls/objects/configuration.rb
|
183
|
+
- lib/controls/objects/configuration_finding.rb
|
184
|
+
- lib/controls/objects/guidance.rb
|
185
|
+
- lib/controls/objects/security_control.rb
|
186
|
+
- lib/controls/objects/security_control_coverage.rb
|
187
|
+
- lib/controls/objects/security_control_finding.rb
|
188
|
+
- lib/controls/objects/threat.rb
|
189
|
+
- lib/controls/objects/threat_vector.rb
|
190
|
+
- lib/controls/objects/trend.rb
|
179
191
|
- lib/controls/response.rb
|
180
|
-
- lib/controls/response/raise_error.rb
|
181
192
|
- lib/controls/version.rb
|
182
193
|
- spec/controls_spec.rb
|
183
194
|
- spec/helper.rb
|
data/lib/controls/error.rb
DELETED
@@ -1,132 +0,0 @@
|
|
1
|
-
module Controls
|
2
|
-
# A namespace for errors in the Controls module
|
3
|
-
class Error < StandardError
|
4
|
-
# @!attribute [r] error_message
|
5
|
-
# @return [String] the error message
|
6
|
-
# @!attribute [r] error_status
|
7
|
-
# @return [Fixnum] the error message
|
8
|
-
# @!attribute [r] error_json
|
9
|
-
# @return [Hash] the JSON body from the error message
|
10
|
-
attr_reader :error_json
|
11
|
-
attr_reader :error_message
|
12
|
-
attr_reader :error_status
|
13
|
-
|
14
|
-
# @raise [BadRequest,Unauthorized,NotFound,InternalServerError] a subclass
|
15
|
-
# of {Controls::Error}
|
16
|
-
# @return [nil] if no error was raised
|
17
|
-
def self.from_response(response)
|
18
|
-
error = case response[:status].to_i
|
19
|
-
when 302
|
20
|
-
# TODO: Nexpose/ControlsInsight returns 302 when you either visit a
|
21
|
-
# none existant path (Nexpose) or unauthenticated
|
22
|
-
# (ControlsInsight).
|
23
|
-
Found # if response[:body].empty?
|
24
|
-
when 400 then BadRequest
|
25
|
-
when 401 then Unauthorized
|
26
|
-
when 404 then NotFound
|
27
|
-
when 500 then InternalServerError
|
28
|
-
end
|
29
|
-
|
30
|
-
error.new(response) if error
|
31
|
-
end
|
32
|
-
|
33
|
-
# @return [self] generates an error and passes it to super
|
34
|
-
def initialize(response = nil)
|
35
|
-
@response = response
|
36
|
-
super(generate_error)
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
# @return [String] an error message to be used by {#generate_error}
|
42
|
-
def response_message
|
43
|
-
return @response_message if @response_message
|
44
|
-
|
45
|
-
resp = @response[:response]
|
46
|
-
|
47
|
-
if resp.headers['content-type']
|
48
|
-
resp.headers['content-type'][/(\S+);charset=utf-8/i]
|
49
|
-
html = Regexp.last_match && Regexp.last_match[1].eql?('text/html')
|
50
|
-
end
|
51
|
-
|
52
|
-
@response_message = if html
|
53
|
-
doc = Nokogiri::XML.parse(resp.body)
|
54
|
-
|
55
|
-
if doc.css('title').text.eql? 'ControlsInsight'
|
56
|
-
message = ['message'].zip(doc.css('h1').map(&:text))
|
57
|
-
reason = ['reason'].zip([doc.css('p').map(&:text)])
|
58
|
-
Hash[message + reason]
|
59
|
-
else
|
60
|
-
Hash[doc.css('HR').children.map { |elem| elem.text.split(' ', 2) }]
|
61
|
-
end
|
62
|
-
else
|
63
|
-
if resp.body.empty?
|
64
|
-
@error_json = {}
|
65
|
-
else
|
66
|
-
@error_json = JSON.parse(resp.body)
|
67
|
-
@error_message = @error_json['message']
|
68
|
-
@error_status = @error_json['status'].to_i
|
69
|
-
|
70
|
-
@error_json
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
# @return [String] the error message passed to super on {#initialize}
|
76
|
-
def generate_error
|
77
|
-
return unless @response
|
78
|
-
|
79
|
-
message = "#{@response[:method]} ".upcase
|
80
|
-
message << "#{@response[:url].path}"
|
81
|
-
|
82
|
-
if response_message.is_a? Hash
|
83
|
-
message << ": #{response_message['message']}\n" if response_message['message']
|
84
|
-
|
85
|
-
if response_message['reason'].respond_to?(:join)
|
86
|
-
message << response_message['reason'].join("\n")
|
87
|
-
elsif response_message['reason']
|
88
|
-
message << response_message['reason'].to_s
|
89
|
-
end
|
90
|
-
elsif response_message.is_a? String
|
91
|
-
message << response_message
|
92
|
-
else
|
93
|
-
message << "#{@response[:status]} -"
|
94
|
-
message << self.class.to_s.split('::', 2).last
|
95
|
-
end
|
96
|
-
|
97
|
-
message.strip
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
# @!group Generic errors
|
102
|
-
|
103
|
-
# TODO REVIEW: To be raised when a user hasn't explicitly supplied credentials or set up
|
104
|
-
# any environment defaults.
|
105
|
-
Unauthenticated = Class.new(StandardError)
|
106
|
-
|
107
|
-
# @!endgroup
|
108
|
-
|
109
|
-
# @!group HTTP errors
|
110
|
-
|
111
|
-
# @return [Found] an error to be raised when a status code of 401 is
|
112
|
-
# returned by the API
|
113
|
-
Found = Class.new(Error)
|
114
|
-
|
115
|
-
# @return [Unauthorized] an error to be raised when a status code of 401 is
|
116
|
-
# returned by the API
|
117
|
-
Unauthorized = Class.new(Error)
|
118
|
-
|
119
|
-
# @return [BadRequest] an error to be raised when a status code of 400 is
|
120
|
-
# returned by the API
|
121
|
-
BadRequest = Class.new(Error)
|
122
|
-
|
123
|
-
# @return [NotFound] an error to be raised when a status code of 404 is
|
124
|
-
# returned by the API
|
125
|
-
NotFound = Class.new(Error)
|
126
|
-
|
127
|
-
# @return [InternalServerError] an error to be raised when a status code of
|
128
|
-
# 500 is returned by the API
|
129
|
-
InternalServerError = Class.new(Error)
|
130
|
-
|
131
|
-
# @!endgroup
|
132
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'controls/error'
|
2
|
-
|
3
|
-
module Controls
|
4
|
-
module Response
|
5
|
-
# A middleware plugin that hooks into the Faraday client used by this gem
|
6
|
-
class RaiseError < Faraday::Response::Middleware
|
7
|
-
private
|
8
|
-
|
9
|
-
# Implements the {#on_complete} hook used by Faraday's middleware
|
10
|
-
#
|
11
|
-
# @raise [Controls::Error] a subclass of Controls::Error if any errors
|
12
|
-
# were encountered
|
13
|
-
# @return [nil] if no error was found
|
14
|
-
def on_complete(response)
|
15
|
-
if error = Controls::Error.from_response(response)
|
16
|
-
raise error
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|