nucleus 0.1.0 → 0.2.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/.rubocop.yml +3 -0
- data/CHANGELOG.md +18 -4
- data/README.md +28 -40
- data/Rakefile +137 -137
- data/config/nucleus_config.rb +0 -4
- data/lib/nucleus/adapter_resolver.rb +115 -115
- data/lib/nucleus/adapters/buildpack_translator.rb +79 -79
- data/lib/nucleus/adapters/v1/cloud_control/application.rb +108 -108
- data/lib/nucleus/adapters/v1/cloud_control/authentication.rb +27 -27
- data/lib/nucleus/adapters/v1/cloud_control/cloud_control.rb +153 -153
- data/lib/nucleus/adapters/v1/cloud_control/domains.rb +68 -68
- data/lib/nucleus/adapters/v1/cloud_control/logs.rb +103 -103
- data/lib/nucleus/adapters/v1/cloud_control/vars.rb +88 -88
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/domains.rb +149 -149
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/logs.rb +303 -303
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/services.rb +286 -286
- data/lib/nucleus/adapters/v1/heroku/heroku.rb +2 -2
- data/lib/nucleus/adapters/v1/heroku/logs.rb +108 -108
- data/lib/nucleus/core/adapter_authentication_inductor.rb +0 -2
- data/lib/nucleus/core/adapter_extensions/auth/http_basic_auth_client.rb +37 -37
- data/lib/nucleus/core/adapter_extensions/http_client.rb +177 -177
- data/lib/nucleus/core/common/files/archive_extractor.rb +112 -112
- data/lib/nucleus/core/common/files/archiver.rb +91 -91
- data/lib/nucleus/core/common/logging/request_log_formatter.rb +48 -48
- data/lib/nucleus/core/error_messages.rb +127 -127
- data/lib/nucleus/core/models/abstract_model.rb +29 -29
- data/lib/nucleus/scripts/load_dependencies.rb +0 -1
- data/lib/nucleus/scripts/setup_config.rb +28 -28
- data/lib/nucleus/version.rb +3 -3
- data/nucleus.gemspec +10 -12
- data/spec/factories/models.rb +63 -61
- data/spec/integration/api/auth_spec.rb +58 -58
- data/spec/test_suites.rake +31 -31
- data/spec/unit/common/helpers/auth_helper_spec.rb +73 -73
- data/spec/unit/common/oauth2_auth_client_spec.rb +1 -1
- data/tasks/compatibility.rake +113 -113
- data/tasks/evaluation.rake +162 -162
- metadata +16 -30
@@ -127,7 +127,6 @@ module Nucleus
|
|
127
127
|
latest_version_id = release[:id]
|
128
128
|
end
|
129
129
|
end
|
130
|
-
latest_version_id
|
131
130
|
else
|
132
131
|
latest_version = 0
|
133
132
|
latest_version_id = nil
|
@@ -137,8 +136,9 @@ module Nucleus
|
|
137
136
|
latest_version_id = dyno[:release][:id]
|
138
137
|
end
|
139
138
|
end
|
140
|
-
latest_version_id
|
141
139
|
end
|
140
|
+
|
141
|
+
latest_version_id
|
142
142
|
end
|
143
143
|
end
|
144
144
|
end
|
@@ -1,108 +1,108 @@
|
|
1
|
-
module Nucleus
|
2
|
-
module Adapters
|
3
|
-
module V1
|
4
|
-
class Heroku < Stub
|
5
|
-
module Logs
|
6
|
-
# Carriage return (newline in Mac OS) + line feed (newline in Unix) == CRLF (newline in Windows)
|
7
|
-
CRLF = "\r\n"
|
8
|
-
|
9
|
-
# @see Stub#logs
|
10
|
-
def logs(application_id)
|
11
|
-
# fails with 404 if application is not available and serves for timestamps
|
12
|
-
app = get("/apps/#{application_id}").body
|
13
|
-
|
14
|
-
available_log_files = []
|
15
|
-
available_log_types.keys.each do |type|
|
16
|
-
# TODO: right now, we always assume the log has recently been updated
|
17
|
-
available_log_files.push(id: type, name: type, type: type,
|
18
|
-
created_at: app[:created_at], updated_at: Time.now.utc.iso8601)
|
19
|
-
end
|
20
|
-
available_log_files
|
21
|
-
end
|
22
|
-
|
23
|
-
# @see Stub#log?
|
24
|
-
def log?(application_id, log_id)
|
25
|
-
# fails with 404 if application is not available
|
26
|
-
get("/apps/#{application_id}")
|
27
|
-
|
28
|
-
return true if log_id.to_sym == :all
|
29
|
-
return true if log_id.to_sym == :build
|
30
|
-
available_log_types.key? log_id.to_sym
|
31
|
-
end
|
32
|
-
|
33
|
-
# @see Stub#log_entries
|
34
|
-
def log_entries(application_id, log_id)
|
35
|
-
unless log?(application_id, log_id)
|
36
|
-
fail Errors::AdapterResourceNotFoundError,
|
37
|
-
"Invalid log file '#{log_id}', not available for application '#{application_id}'"
|
38
|
-
end
|
39
|
-
|
40
|
-
return build_log_entries(application_id) if log_id.to_sym == Enums::ApplicationLogfileType::BUILD
|
41
|
-
|
42
|
-
request_body = request_body(log_id.to_sym).merge(tail: false)
|
43
|
-
log = post("/apps/#{application_id}/log-sessions", body: request_body).body
|
44
|
-
logfile = get(log[:logplex_url], headers: {}).body
|
45
|
-
# process to entries
|
46
|
-
entries = []
|
47
|
-
# skip empty logs, which are detected as Hash by the http client
|
48
|
-
logfile.split(CRLF).each { |logfile_line| entries.push logfile_line } unless logfile == {}
|
49
|
-
entries
|
50
|
-
end
|
51
|
-
|
52
|
-
# @see Stub#tail
|
53
|
-
def tail(application_id, log_id, stream)
|
54
|
-
# Currently no tailing for build log possible
|
55
|
-
if log_id == Enums::ApplicationLogfileType::BUILD
|
56
|
-
entries = build_log_entries(application_id)
|
57
|
-
entries.each { |entry| stream.send_message(entry) }
|
58
|
-
stream.close
|
59
|
-
else
|
60
|
-
request_body = request_body(log_id.to_sym).merge(tail: true)
|
61
|
-
log = post("/apps/#{application_id}/log-sessions", body: request_body).body
|
62
|
-
tail_http_response(log[:logplex_url], stream)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def available_log_types
|
69
|
-
log_types = {}
|
70
|
-
log_types[Enums::ApplicationLogfileType::API] = { source: 'heroku', dyno: 'api' }
|
71
|
-
log_types[Enums::ApplicationLogfileType::APPLICATION] = { source: 'app' }
|
72
|
-
log_types[Enums::ApplicationLogfileType::REQUEST] = { source: 'heroku', dyno: 'router' }
|
73
|
-
# TODO: filter only for web and worker dynos (must be merged manually :/)
|
74
|
-
log_types[Enums::ApplicationLogfileType::SYSTEM] = { source: 'heroku' }
|
75
|
-
log_types
|
76
|
-
end
|
77
|
-
|
78
|
-
def request_body(log_id)
|
79
|
-
return {} if log_id == :all
|
80
|
-
available_log_types[log_id]
|
81
|
-
end
|
82
|
-
|
83
|
-
def build_log_entries(application_id)
|
84
|
-
build_list = get("/apps/#{application_id}/builds").body
|
85
|
-
# limitation: show only the last 3 builds
|
86
|
-
entries = []
|
87
|
-
build_list.last(3).each do |build|
|
88
|
-
entries.push(*build_result_entries(application_id, build[:id]))
|
89
|
-
end
|
90
|
-
entries
|
91
|
-
end
|
92
|
-
|
93
|
-
def build_result_entries(application_id, build_id)
|
94
|
-
build_result = get("/apps/#{application_id}/builds/#{build_id}/result").body
|
95
|
-
entries = []
|
96
|
-
build_result[:lines].each do |line_entry|
|
97
|
-
# skip all blank lines
|
98
|
-
next if line_entry[:line].strip.empty?
|
99
|
-
# push and remove all trailing newline characters
|
100
|
-
entries.push line_entry[:line].chomp('')
|
101
|
-
end
|
102
|
-
entries
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
1
|
+
module Nucleus
|
2
|
+
module Adapters
|
3
|
+
module V1
|
4
|
+
class Heroku < Stub
|
5
|
+
module Logs
|
6
|
+
# Carriage return (newline in Mac OS) + line feed (newline in Unix) == CRLF (newline in Windows)
|
7
|
+
CRLF = "\r\n".freeze
|
8
|
+
|
9
|
+
# @see Stub#logs
|
10
|
+
def logs(application_id)
|
11
|
+
# fails with 404 if application is not available and serves for timestamps
|
12
|
+
app = get("/apps/#{application_id}").body
|
13
|
+
|
14
|
+
available_log_files = []
|
15
|
+
available_log_types.keys.each do |type|
|
16
|
+
# TODO: right now, we always assume the log has recently been updated
|
17
|
+
available_log_files.push(id: type, name: type, type: type,
|
18
|
+
created_at: app[:created_at], updated_at: Time.now.utc.iso8601)
|
19
|
+
end
|
20
|
+
available_log_files
|
21
|
+
end
|
22
|
+
|
23
|
+
# @see Stub#log?
|
24
|
+
def log?(application_id, log_id)
|
25
|
+
# fails with 404 if application is not available
|
26
|
+
get("/apps/#{application_id}")
|
27
|
+
|
28
|
+
return true if log_id.to_sym == :all
|
29
|
+
return true if log_id.to_sym == :build
|
30
|
+
available_log_types.key? log_id.to_sym
|
31
|
+
end
|
32
|
+
|
33
|
+
# @see Stub#log_entries
|
34
|
+
def log_entries(application_id, log_id)
|
35
|
+
unless log?(application_id, log_id)
|
36
|
+
fail Errors::AdapterResourceNotFoundError,
|
37
|
+
"Invalid log file '#{log_id}', not available for application '#{application_id}'"
|
38
|
+
end
|
39
|
+
|
40
|
+
return build_log_entries(application_id) if log_id.to_sym == Enums::ApplicationLogfileType::BUILD
|
41
|
+
|
42
|
+
request_body = request_body(log_id.to_sym).merge(tail: false)
|
43
|
+
log = post("/apps/#{application_id}/log-sessions", body: request_body).body
|
44
|
+
logfile = get(log[:logplex_url], headers: {}).body
|
45
|
+
# process to entries
|
46
|
+
entries = []
|
47
|
+
# skip empty logs, which are detected as Hash by the http client
|
48
|
+
logfile.split(CRLF).each { |logfile_line| entries.push logfile_line } unless logfile == {}
|
49
|
+
entries
|
50
|
+
end
|
51
|
+
|
52
|
+
# @see Stub#tail
|
53
|
+
def tail(application_id, log_id, stream)
|
54
|
+
# Currently no tailing for build log possible
|
55
|
+
if log_id == Enums::ApplicationLogfileType::BUILD
|
56
|
+
entries = build_log_entries(application_id)
|
57
|
+
entries.each { |entry| stream.send_message(entry) }
|
58
|
+
stream.close
|
59
|
+
else
|
60
|
+
request_body = request_body(log_id.to_sym).merge(tail: true)
|
61
|
+
log = post("/apps/#{application_id}/log-sessions", body: request_body).body
|
62
|
+
tail_http_response(log[:logplex_url], stream)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def available_log_types
|
69
|
+
log_types = {}
|
70
|
+
log_types[Enums::ApplicationLogfileType::API] = { source: 'heroku', dyno: 'api' }
|
71
|
+
log_types[Enums::ApplicationLogfileType::APPLICATION] = { source: 'app' }
|
72
|
+
log_types[Enums::ApplicationLogfileType::REQUEST] = { source: 'heroku', dyno: 'router' }
|
73
|
+
# TODO: filter only for web and worker dynos (must be merged manually :/)
|
74
|
+
log_types[Enums::ApplicationLogfileType::SYSTEM] = { source: 'heroku' }
|
75
|
+
log_types
|
76
|
+
end
|
77
|
+
|
78
|
+
def request_body(log_id)
|
79
|
+
return {} if log_id == :all
|
80
|
+
available_log_types[log_id]
|
81
|
+
end
|
82
|
+
|
83
|
+
def build_log_entries(application_id)
|
84
|
+
build_list = get("/apps/#{application_id}/builds").body
|
85
|
+
# limitation: show only the last 3 builds
|
86
|
+
entries = []
|
87
|
+
build_list.last(3).each do |build|
|
88
|
+
entries.push(*build_result_entries(application_id, build[:id]))
|
89
|
+
end
|
90
|
+
entries
|
91
|
+
end
|
92
|
+
|
93
|
+
def build_result_entries(application_id, build_id)
|
94
|
+
build_result = get("/apps/#{application_id}/builds/#{build_id}/result").body
|
95
|
+
entries = []
|
96
|
+
build_result[:lines].each do |line_entry|
|
97
|
+
# skip all blank lines
|
98
|
+
next if line_entry[:line].strip.empty?
|
99
|
+
# push and remove all trailing newline characters
|
100
|
+
entries.push line_entry[:line].chomp('')
|
101
|
+
end
|
102
|
+
entries
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -15,8 +15,6 @@ module Nucleus
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
private
|
19
|
-
|
20
18
|
# Patch the actual method that is defined in an API version stub.
|
21
19
|
# The method shall than be able to update the authentication token if the initial authentication expired.<br>
|
22
20
|
# Only major authentication issues, e.g. if the credentials are repeatedly rejected,
|
@@ -1,37 +1,37 @@
|
|
1
|
-
module Nucleus
|
2
|
-
module Adapters
|
3
|
-
# Implementation of the AuthClient that works with the HTTP basic authentication.
|
4
|
-
class HttpBasicAuthClient < AuthClient
|
5
|
-
# Create a new instance of an {HttpBasicAuthClient}.
|
6
|
-
# @param [Boolean] check_certificates true if SSL certificates are to be validated,
|
7
|
-
# false if they are to be ignored (e.g. when using self-signed certificates in development environments)
|
8
|
-
# @yield [verify_ssl, username, password] Auth credentials verification block,
|
9
|
-
# must check if the combination of username and password is accepted by the endpoint.
|
10
|
-
# @yieldparam [Hash<String,String>] headers headers for an HTTP request,
|
11
|
-
# including the authentication header to be tested
|
12
|
-
# @yieldreturn [Boolean] true if the authentication was verified to be ok,
|
13
|
-
# false if an error occurred, e.g. with bad credentials
|
14
|
-
def initialize(check_certificates = true, &verification)
|
15
|
-
@verification = verification
|
16
|
-
super(check_certificates)
|
17
|
-
end
|
18
|
-
|
19
|
-
# @see AuthClient#authenticate
|
20
|
-
def authenticate(username, password)
|
21
|
-
packed_credentials = ["#{username}:#{password}"].pack('m*').
|
22
|
-
valid = @verification.call(verify_ssl, 'Authorization' => "Basic #{packed_credentials}")
|
23
|
-
fail Errors::EndpointAuthenticationError, 'Authentication failed, credentials seem to be invalid' unless valid
|
24
|
-
# verification passed, credentials are valid
|
25
|
-
@packed_credentials = packed_credentials
|
26
|
-
self
|
27
|
-
end
|
28
|
-
|
29
|
-
# @see AuthClient#auth_header
|
30
|
-
def auth_header
|
31
|
-
fail Errors::EndpointAuthenticationError,
|
32
|
-
'Authentication client was not authenticated yet' unless @packed_credentials
|
33
|
-
{ 'Authorization' => "Basic #{@packed_credentials}" }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
1
|
+
module Nucleus
|
2
|
+
module Adapters
|
3
|
+
# Implementation of the AuthClient that works with the HTTP basic authentication.
|
4
|
+
class HttpBasicAuthClient < AuthClient
|
5
|
+
# Create a new instance of an {HttpBasicAuthClient}.
|
6
|
+
# @param [Boolean] check_certificates true if SSL certificates are to be validated,
|
7
|
+
# false if they are to be ignored (e.g. when using self-signed certificates in development environments)
|
8
|
+
# @yield [verify_ssl, username, password] Auth credentials verification block,
|
9
|
+
# must check if the combination of username and password is accepted by the endpoint.
|
10
|
+
# @yieldparam [Hash<String,String>] headers headers for an HTTP request,
|
11
|
+
# including the authentication header to be tested
|
12
|
+
# @yieldreturn [Boolean] true if the authentication was verified to be ok,
|
13
|
+
# false if an error occurred, e.g. with bad credentials
|
14
|
+
def initialize(check_certificates = true, &verification)
|
15
|
+
@verification = verification
|
16
|
+
super(check_certificates)
|
17
|
+
end
|
18
|
+
|
19
|
+
# @see AuthClient#authenticate
|
20
|
+
def authenticate(username, password)
|
21
|
+
packed_credentials = ["#{username}:#{password}"].pack('m*').delete("\n")
|
22
|
+
valid = @verification.call(verify_ssl, 'Authorization' => "Basic #{packed_credentials}")
|
23
|
+
fail Errors::EndpointAuthenticationError, 'Authentication failed, credentials seem to be invalid' unless valid
|
24
|
+
# verification passed, credentials are valid
|
25
|
+
@packed_credentials = packed_credentials
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
# @see AuthClient#auth_header
|
30
|
+
def auth_header
|
31
|
+
fail Errors::EndpointAuthenticationError,
|
32
|
+
'Authentication client was not authenticated yet' unless @packed_credentials
|
33
|
+
{ 'Authorization' => "Basic #{@packed_credentials}" }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,177 +1,177 @@
|
|
1
|
-
module Nucleus
|
2
|
-
module Adapters
|
3
|
-
module HttpClient
|
4
|
-
# Executes a HEAD request to the given URL.
|
5
|
-
#
|
6
|
-
# @param [String] path path to add to the endpoint URL
|
7
|
-
# @param [Hash] params options to call the post request with
|
8
|
-
# @option params [Array<int>] :expects ([200]) http status code that is expected
|
9
|
-
# @option params [Hash] :headers request headers to use with the request
|
10
|
-
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
11
|
-
# unprocessed response
|
12
|
-
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
13
|
-
def head(path, params = {})
|
14
|
-
execute_request(:head, [200], path, params, params.delete(:native_call) { false })
|
15
|
-
end
|
16
|
-
|
17
|
-
# Executes a GET request to the given URL.
|
18
|
-
#
|
19
|
-
# @param [String] path path to add to the endpoint URL
|
20
|
-
# @param [Hash] params options to call the post request with
|
21
|
-
# @option params [Array<int>] :expects ([200]) http status code that is expected
|
22
|
-
# @option params [Hash] :headers request headers to use with the request
|
23
|
-
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
24
|
-
# unprocessed response
|
25
|
-
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
26
|
-
def get(path, params = {})
|
27
|
-
execute_request(:get, [200], path, params, params.delete(:native_call) { false })
|
28
|
-
end
|
29
|
-
|
30
|
-
# Executes a POST request to the given URL.
|
31
|
-
#
|
32
|
-
# @param [String] path path to add to the endpoint URL
|
33
|
-
# @param [Hash] params options to call the post request with
|
34
|
-
# @option params [Array<int>] :expects ([200,201]) http status code that is expected
|
35
|
-
# @option params [Hash] :body request body, will be converted to json format
|
36
|
-
# @option params [Hash] :headers request headers to use with the request
|
37
|
-
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
38
|
-
# unprocessed response
|
39
|
-
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
40
|
-
def post(path, params = {})
|
41
|
-
execute_request(:post, [200, 201], path, params, params.delete(:native_call) { false })
|
42
|
-
end
|
43
|
-
|
44
|
-
# Executes a PATCH request to the given URL.
|
45
|
-
#
|
46
|
-
# @param [String] path path to add to the endpoint URL
|
47
|
-
# @param [Hash] params options to call the post request with
|
48
|
-
# @option params [Array<int>] :expects ([200,201]) http status code that is expected
|
49
|
-
# @option params [Hash] :body request body, will be converted to json format
|
50
|
-
# @option params [Hash] :headers request headers to use with the request
|
51
|
-
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
52
|
-
# unprocessed response
|
53
|
-
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
54
|
-
def patch(path, params = {})
|
55
|
-
execute_request(:patch, [200, 201], path, params, params.delete(:native_call) { false })
|
56
|
-
end
|
57
|
-
|
58
|
-
# Executes a PUT request to the given URL.
|
59
|
-
#
|
60
|
-
# @param [String] path path to add to the endpoint URL
|
61
|
-
# @param [Hash] params options to call the post request with
|
62
|
-
# @option params [Array<int>] :expects ([200,201]) http status code that is expected
|
63
|
-
# @option params [Hash] :body request body, will be converted to json format
|
64
|
-
# @option params [Hash] :headers request headers to use with the request
|
65
|
-
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
66
|
-
# unprocessed response
|
67
|
-
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
68
|
-
def put(path, params = {})
|
69
|
-
execute_request(:put, [200, 201], path, params, params.delete(:native_call) { false })
|
70
|
-
end
|
71
|
-
|
72
|
-
# Executes a DELETE request to the given URL.
|
73
|
-
#
|
74
|
-
# @param [String] path path to add to the endpoint URL
|
75
|
-
# @param [Hash] params options to call the post request with
|
76
|
-
# @option params [Array<int>] :expects ([200,204]) http status code that is expected
|
77
|
-
# @option params [Hash] :headers request headers to use with the request
|
78
|
-
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
79
|
-
# unprocessed response
|
80
|
-
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
81
|
-
def delete(path, params = {})
|
82
|
-
execute_request(:delete, [200, 204], path, params, params.delete(:native_call) { false })
|
83
|
-
end
|
84
|
-
|
85
|
-
private
|
86
|
-
|
87
|
-
def execute_request(method, default_expect, path, params, native_call = false)
|
88
|
-
params[:expects] = default_expect unless params.key? :expects
|
89
|
-
params[:method] = method
|
90
|
-
|
91
|
-
url = Regexp::PERFECT_URL_PATTERN =~ path ? path : to_url(path)
|
92
|
-
response = Excon.new(url, excon_connection_params(params)).request(add_common_request_params(params))
|
93
|
-
# we never want the JSON string, but always the hash representation
|
94
|
-
response.body = hash_of(response.body)
|
95
|
-
response
|
96
|
-
rescue Excon::Errors::HTTPStatusError => e
|
97
|
-
handle_execute_request_error(e, url, native_call)
|
98
|
-
end
|
99
|
-
|
100
|
-
def handle_execute_request_error(e, url, native_call)
|
101
|
-
log.debug 'ERROR, Excon could not execute the request.'
|
102
|
-
# transform json response to Hash object
|
103
|
-
e.response.body = hash_of(e.response.body)
|
104
|
-
|
105
|
-
# if this is a native API call, do not further process the error
|
106
|
-
return e.response if native_call
|
107
|
-
|
108
|
-
# fail with adapter specific error handling
|
109
|
-
handle_error(e.response) if respond_to?(:handle_error)
|
110
|
-
fallback_error_handling(e, url)
|
111
|
-
end
|
112
|
-
|
113
|
-
def fallback_error_handling(e, url)
|
114
|
-
error_status = e.response.status
|
115
|
-
# arriving here, error could not be processed --> use fallback errors
|
116
|
-
if e.is_a? Excon::Errors::ServerError
|
117
|
-
fail Errors::UnknownAdapterCallError, e.message
|
118
|
-
elsif error_status == 404
|
119
|
-
log.error("Resource not found (404) at '#{url}', indicating an adapter issue")
|
120
|
-
fail Errors::UnknownAdapterCallError, 'Resource not found, probably the adapter must be updated'
|
121
|
-
elsif error_status == 401
|
122
|
-
fail Errors::EndpointAuthenticationError,
|
123
|
-
'Auth. failed, probably cache is outdated or permissions were revoked?'
|
124
|
-
else
|
125
|
-
log.error("Fallback error handling (#{error_status}) at '#{url}', indicating an adapter issue")
|
126
|
-
fail Errors::UnknownAdapterCallError, e.message
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def to_url(path)
|
131
|
-
# insert missing slash, prevent double slashes
|
132
|
-
return "#{@endpoint_url}/#{path}" unless @endpoint_url.end_with?('/') || path.start_with?('/')
|
133
|
-
"#{@endpoint_url}#{path}"
|
134
|
-
end
|
135
|
-
|
136
|
-
def excon_connection_params(params)
|
137
|
-
middleware = Excon.defaults[:middlewares].dup
|
138
|
-
|
139
|
-
if params[:follow_redirects] == false
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
Excon::Middleware::Decompress].push(*middleware).uniq
|
144
|
-
|
145
|
-
{ middlewares: middleware, ssl_verify_peer: @check_certificates }
|
146
|
-
end
|
147
|
-
|
148
|
-
def hash_of(message_body)
|
149
|
-
return {} if message_body.nil? || message_body.empty?
|
150
|
-
begin
|
151
|
-
return Oj.load(message_body, symbol_keys: true)
|
152
|
-
rescue Oj::Error
|
153
|
-
# parsing failed, content probably is no valid JSON content
|
154
|
-
message_body
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def add_common_request_params(params)
|
159
|
-
common_params = { connection_timeout: 610, write_timeout: 600, read_timeout: 600 }
|
160
|
-
# allow to follow redirects in the APIs
|
161
|
-
allowed_status_codes = params.key?(:expects) ? [*params[:expects]] : []
|
162
|
-
unless params[:follow_redirects] == false
|
163
|
-
allowed_status_codes.push(*[301, 302, 303, 307, 308])
|
164
|
-
end
|
165
|
-
|
166
|
-
params[:expects] = allowed_status_codes.uniq
|
167
|
-
# use default or customized headers
|
168
|
-
params[:headers] = headers unless params[:headers]
|
169
|
-
# specify encoding if not done yet: use only gzip since deflate does cause issues with VCR cassettes in tests
|
170
|
-
params[:headers]['Accept-Encoding'] = 'gzip' unless params[:headers].key? 'Accept-Encoding'
|
171
|
-
params[:body] = params[:body].to_json if params.key? :body
|
172
|
-
# merge and return
|
173
|
-
common_params.merge params
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
1
|
+
module Nucleus
|
2
|
+
module Adapters
|
3
|
+
module HttpClient
|
4
|
+
# Executes a HEAD request to the given URL.
|
5
|
+
#
|
6
|
+
# @param [String] path path to add to the endpoint URL
|
7
|
+
# @param [Hash] params options to call the post request with
|
8
|
+
# @option params [Array<int>] :expects ([200]) http status code that is expected
|
9
|
+
# @option params [Hash] :headers request headers to use with the request
|
10
|
+
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
11
|
+
# unprocessed response
|
12
|
+
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
13
|
+
def head(path, params = {})
|
14
|
+
execute_request(:head, [200], path, params, params.delete(:native_call) { false })
|
15
|
+
end
|
16
|
+
|
17
|
+
# Executes a GET request to the given URL.
|
18
|
+
#
|
19
|
+
# @param [String] path path to add to the endpoint URL
|
20
|
+
# @param [Hash] params options to call the post request with
|
21
|
+
# @option params [Array<int>] :expects ([200]) http status code that is expected
|
22
|
+
# @option params [Hash] :headers request headers to use with the request
|
23
|
+
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
24
|
+
# unprocessed response
|
25
|
+
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
26
|
+
def get(path, params = {})
|
27
|
+
execute_request(:get, [200], path, params, params.delete(:native_call) { false })
|
28
|
+
end
|
29
|
+
|
30
|
+
# Executes a POST request to the given URL.
|
31
|
+
#
|
32
|
+
# @param [String] path path to add to the endpoint URL
|
33
|
+
# @param [Hash] params options to call the post request with
|
34
|
+
# @option params [Array<int>] :expects ([200,201]) http status code that is expected
|
35
|
+
# @option params [Hash] :body request body, will be converted to json format
|
36
|
+
# @option params [Hash] :headers request headers to use with the request
|
37
|
+
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
38
|
+
# unprocessed response
|
39
|
+
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
40
|
+
def post(path, params = {})
|
41
|
+
execute_request(:post, [200, 201], path, params, params.delete(:native_call) { false })
|
42
|
+
end
|
43
|
+
|
44
|
+
# Executes a PATCH request to the given URL.
|
45
|
+
#
|
46
|
+
# @param [String] path path to add to the endpoint URL
|
47
|
+
# @param [Hash] params options to call the post request with
|
48
|
+
# @option params [Array<int>] :expects ([200,201]) http status code that is expected
|
49
|
+
# @option params [Hash] :body request body, will be converted to json format
|
50
|
+
# @option params [Hash] :headers request headers to use with the request
|
51
|
+
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
52
|
+
# unprocessed response
|
53
|
+
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
54
|
+
def patch(path, params = {})
|
55
|
+
execute_request(:patch, [200, 201], path, params, params.delete(:native_call) { false })
|
56
|
+
end
|
57
|
+
|
58
|
+
# Executes a PUT request to the given URL.
|
59
|
+
#
|
60
|
+
# @param [String] path path to add to the endpoint URL
|
61
|
+
# @param [Hash] params options to call the post request with
|
62
|
+
# @option params [Array<int>] :expects ([200,201]) http status code that is expected
|
63
|
+
# @option params [Hash] :body request body, will be converted to json format
|
64
|
+
# @option params [Hash] :headers request headers to use with the request
|
65
|
+
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
66
|
+
# unprocessed response
|
67
|
+
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
68
|
+
def put(path, params = {})
|
69
|
+
execute_request(:put, [200, 201], path, params, params.delete(:native_call) { false })
|
70
|
+
end
|
71
|
+
|
72
|
+
# Executes a DELETE request to the given URL.
|
73
|
+
#
|
74
|
+
# @param [String] path path to add to the endpoint URL
|
75
|
+
# @param [Hash] params options to call the post request with
|
76
|
+
# @option params [Array<int>] :expects ([200,204]) http status code that is expected
|
77
|
+
# @option params [Hash] :headers request headers to use with the request
|
78
|
+
# @option params [Boolean] :native_call if true the request is a native API call and shall return the
|
79
|
+
# unprocessed response
|
80
|
+
# @raise [Nucleus::Errors::AdapterError] if the call failed and did not return the expected code(s)
|
81
|
+
def delete(path, params = {})
|
82
|
+
execute_request(:delete, [200, 204], path, params, params.delete(:native_call) { false })
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def execute_request(method, default_expect, path, params, native_call = false)
|
88
|
+
params[:expects] = default_expect unless params.key? :expects
|
89
|
+
params[:method] = method
|
90
|
+
|
91
|
+
url = Regexp::PERFECT_URL_PATTERN =~ path ? path : to_url(path)
|
92
|
+
response = Excon.new(url, excon_connection_params(params)).request(add_common_request_params(params))
|
93
|
+
# we never want the JSON string, but always the hash representation
|
94
|
+
response.body = hash_of(response.body)
|
95
|
+
response
|
96
|
+
rescue Excon::Errors::HTTPStatusError => e
|
97
|
+
handle_execute_request_error(e, url, native_call)
|
98
|
+
end
|
99
|
+
|
100
|
+
def handle_execute_request_error(e, url, native_call)
|
101
|
+
log.debug 'ERROR, Excon could not execute the request.'
|
102
|
+
# transform json response to Hash object
|
103
|
+
e.response.body = hash_of(e.response.body)
|
104
|
+
|
105
|
+
# if this is a native API call, do not further process the error
|
106
|
+
return e.response if native_call
|
107
|
+
|
108
|
+
# fail with adapter specific error handling
|
109
|
+
handle_error(e.response) if respond_to?(:handle_error)
|
110
|
+
fallback_error_handling(e, url)
|
111
|
+
end
|
112
|
+
|
113
|
+
def fallback_error_handling(e, url)
|
114
|
+
error_status = e.response.status
|
115
|
+
# arriving here, error could not be processed --> use fallback errors
|
116
|
+
if e.is_a? Excon::Errors::ServerError
|
117
|
+
fail Errors::UnknownAdapterCallError, e.message
|
118
|
+
elsif error_status == 404
|
119
|
+
log.error("Resource not found (404) at '#{url}', indicating an adapter issue")
|
120
|
+
fail Errors::UnknownAdapterCallError, 'Resource not found, probably the adapter must be updated'
|
121
|
+
elsif error_status == 401
|
122
|
+
fail Errors::EndpointAuthenticationError,
|
123
|
+
'Auth. failed, probably cache is outdated or permissions were revoked?'
|
124
|
+
else
|
125
|
+
log.error("Fallback error handling (#{error_status}) at '#{url}', indicating an adapter issue")
|
126
|
+
fail Errors::UnknownAdapterCallError, e.message
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_url(path)
|
131
|
+
# insert missing slash, prevent double slashes
|
132
|
+
return "#{@endpoint_url}/#{path}" unless @endpoint_url.end_with?('/') || path.start_with?('/')
|
133
|
+
"#{@endpoint_url}#{path}"
|
134
|
+
end
|
135
|
+
|
136
|
+
def excon_connection_params(params)
|
137
|
+
middleware = Excon.defaults[:middlewares].dup
|
138
|
+
|
139
|
+
middleware = if params[:follow_redirects] == false
|
140
|
+
[Excon::Middleware::ResponseParser, Excon::Middleware::Decompress].push(*middleware).uniq
|
141
|
+
else
|
142
|
+
[Excon::Middleware::ResponseParser, Excon::Middleware::RedirectFollower,
|
143
|
+
Excon::Middleware::Decompress].push(*middleware).uniq
|
144
|
+
end
|
145
|
+
{ middlewares: middleware, ssl_verify_peer: @check_certificates }
|
146
|
+
end
|
147
|
+
|
148
|
+
def hash_of(message_body)
|
149
|
+
return {} if message_body.nil? || message_body.empty?
|
150
|
+
begin
|
151
|
+
return Oj.load(message_body, symbol_keys: true)
|
152
|
+
rescue Oj::Error
|
153
|
+
# parsing failed, content probably is no valid JSON content
|
154
|
+
message_body
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def add_common_request_params(params)
|
159
|
+
common_params = { connection_timeout: 610, write_timeout: 600, read_timeout: 600 }
|
160
|
+
# allow to follow redirects in the APIs
|
161
|
+
allowed_status_codes = params.key?(:expects) ? [*params[:expects]] : []
|
162
|
+
unless params[:follow_redirects] == false
|
163
|
+
allowed_status_codes.push(*[301, 302, 303, 307, 308])
|
164
|
+
end
|
165
|
+
|
166
|
+
params[:expects] = allowed_status_codes.uniq
|
167
|
+
# use default or customized headers
|
168
|
+
params[:headers] = headers unless params[:headers]
|
169
|
+
# specify encoding if not done yet: use only gzip since deflate does cause issues with VCR cassettes in tests
|
170
|
+
params[:headers]['Accept-Encoding'] = 'gzip' unless params[:headers].key? 'Accept-Encoding'
|
171
|
+
params[:body] = params[:body].to_json if params.key? :body
|
172
|
+
# merge and return
|
173
|
+
common_params.merge params
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|