controls 1.2.0 → 1.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 +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
|