ncore 2.3.3 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -23
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/example/lib/my_api/api_config.rb +8 -0
- data/example/lib/my_api/rails/railtie.rb +4 -0
- data/lib/ncore/associations.rb +13 -13
- data/lib/ncore/attributes.rb +34 -15
- data/lib/ncore/base.rb +5 -3
- data/lib/ncore/client.rb +35 -33
- data/lib/ncore/client_cache.rb +48 -0
- data/lib/ncore/configuration.rb +17 -25
- data/lib/ncore/exceptions.rb +7 -2
- data/lib/ncore/identity.rb +27 -0
- data/lib/ncore/lifecycle.rb +3 -3
- data/lib/ncore/methods/all.rb +3 -7
- data/lib/ncore/methods/build.rb +2 -2
- data/lib/ncore/methods/count.rb +1 -1
- data/lib/ncore/methods/create.rb +4 -5
- data/lib/ncore/methods/delete.rb +6 -7
- data/lib/ncore/methods/delete_bulk.rb +4 -4
- data/lib/ncore/methods/delete_single.rb +4 -5
- data/lib/ncore/methods/find.rb +5 -6
- data/lib/ncore/methods/find_single.rb +5 -9
- data/lib/ncore/methods/update.rb +6 -7
- data/lib/ncore/rails/active_model.rb +24 -14
- data/lib/ncore/rails/module_fix.rb +5 -3
- data/lib/ncore/singleton_base.rb +4 -3
- data/lib/ncore/ssl/ca-certificates.crt +3287 -0
- data/lib/ncore/util.rb +10 -2
- data/lib/ncore/version.rb +1 -1
- data/lib/ncore.rb +30 -3
- data/ncore.gemspec +1 -1
- metadata +13 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 63b5e71f6a7f803b1a270e81b5f443f41490274387873395dbcc9c05a5cdcca2
|
4
|
+
data.tar.gz: 875e34f54fd4ba0e014c91ab371be8b6d1202e908cd6470b11666e12e82577b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06c85c6685ae41485761e725fb370688846df1b38e5097cf60222f97c7040f2b94f74d250fb37efdc8feff8dbe3b15ca8e366ff2c9f87204de36bea395ee5d2a
|
7
|
+
data.tar.gz: f5ae351e39942821b41e95a6ae17baa9536696a806847c98e693f3526345c999ef42b9babee6b25fcbae5cf79efdaebd4341c14f01f4c91c6251b8a8320c7155
|
data/CHANGELOG.md
CHANGED
@@ -1,25 +1,39 @@
|
|
1
|
-
####
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
1
|
+
#### 3.0.0
|
2
|
+
|
3
|
+
BREAKING CHANGES
|
4
|
+
- Update has_many, belongs_to signatures
|
5
|
+
- Rename Base#url -> #resource_path
|
6
|
+
- Drop ActiveModel <= 4.1
|
7
|
+
- `#errors` is now always an ActiveModel::Errors instance
|
8
|
+
|
9
|
+
DEPRECATION NOTICE
|
10
|
+
- ValidationError is deprecated and will be removed in 3.1.
|
11
|
+
|
12
|
+
Other changes
|
13
|
+
- Add :cache option for requests
|
14
|
+
Set default store at MyApi::Api.cache_store=
|
15
|
+
See example railtie.rb for auto-config
|
16
|
+
Examples:
|
17
|
+
SomeResource.all(cache: true)
|
18
|
+
uses MyApi::Api.cache_store
|
19
|
+
SomeResource.find(id, cache: {expires_in: 5.minutes})
|
20
|
+
uses MyApi::Api.cache_store with specified options
|
21
|
+
SomeResource.find(id, cache: Dalli::Store.new(...))
|
22
|
+
uses provided cache store (with its default options)
|
23
|
+
- Make bearer_credential_key allow strings or symbols
|
24
|
+
- Warn on attr name collision
|
25
|
+
- Update CA certificates
|
26
|
+
- Better default output for #as_json
|
27
|
+
- Allow ActiveModel/Support 6.0
|
28
|
+
- Resolve deprecation messages on Ruby 2.6
|
29
|
+
- Add #factory
|
30
|
+
- API response :errors may be hash or array
|
31
|
+
- Add RecordInvalid#errors
|
32
|
+
- Better Ruby and ActiveModel integration
|
33
|
+
- #eql?, #==, #hash
|
34
|
+
- #model_name
|
35
|
+
- #i18n_scope, config via Api.i18n_scope=
|
36
|
+
- #cache_key, #cache_version, #cache_key_with_version
|
23
37
|
|
24
38
|
#### 2.2.1
|
25
39
|
|
@@ -110,4 +124,4 @@
|
|
110
124
|
|
111
125
|
#### 1.0.0
|
112
126
|
|
113
|
-
- Initial release
|
127
|
+
- Initial release
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
NCore is a Ruby gem designed to help build REST API clients. It is not an API
|
4
4
|
client by itself, but provides several useful building blocks to build one.
|
5
5
|
|
6
|
-
It relies on `excon` for HTTP handling and `
|
6
|
+
It relies on `excon` for HTTP handling and `activemodel`.
|
7
7
|
|
8
8
|
If present, uses `multi_json`. Otherwise, the stdlib 'json' is used.
|
9
9
|
'multi_json' with an accelerated json gem is recommended.
|
@@ -21,6 +21,8 @@ module MyApi
|
|
21
21
|
|
22
22
|
self.strict_attributes = false
|
23
23
|
|
24
|
+
self.i18n_scope = :my_api
|
25
|
+
|
24
26
|
self.instrument_key = 'request.my_api'
|
25
27
|
|
26
28
|
self.status_page = 'http://my.api.status.page'
|
@@ -30,6 +32,12 @@ module MyApi
|
|
30
32
|
# self.bearer_credential_key = :api_key
|
31
33
|
|
32
34
|
self.credentials_error_message = %Q{Missing API credentials. Set default credentials using "MyApi.credentials = {api_user: YOUR_API_USER, api_key: YOUR_API_KEY}"}
|
35
|
+
|
36
|
+
# self.verify_ssl = true
|
37
|
+
|
38
|
+
# self.ssl_cert_bundle = 'path/to/bundle.crt'
|
39
|
+
|
40
|
+
# self.logger = Logger.new(STDOUT)
|
33
41
|
end
|
34
42
|
|
35
43
|
end
|
data/lib/ncore/associations.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
module NCore
|
2
2
|
module Associations
|
3
3
|
|
4
|
-
def has_many(assoc,
|
4
|
+
def has_many(assoc, class_name: nil)
|
5
5
|
assoc = assoc.to_s
|
6
|
-
klass
|
6
|
+
klass = class_name || "#{module_name}::#{assoc.camelize.singularize}"
|
7
7
|
key = "#{attrib_name}_id"
|
8
8
|
class_eval <<-M1, __FILE__, __LINE__+1
|
9
9
|
def #{assoc}(params={})
|
10
10
|
return [] unless id
|
11
11
|
reload = params.delete :reload
|
12
|
-
params = parse_request_params(params).reverse_merge credentials: api_creds
|
13
12
|
cacheable = params.except(:credentials, :request).empty?
|
14
|
-
params.
|
13
|
+
params = parse_request_params(params).reverse_merge credentials: api_creds
|
14
|
+
params[:#{key}] = id
|
15
15
|
if cacheable
|
16
16
|
# only cache unfiltered, default api call
|
17
17
|
@attribs[:#{assoc}] = (!reload && @attribs[:#{assoc}]) || #{klass}.all(params)
|
@@ -24,7 +24,7 @@ module NCore
|
|
24
24
|
def find_#{assoc.singularize}(aid, params={})
|
25
25
|
raise UnsavedObjectError unless id
|
26
26
|
params = parse_request_params(params).reverse_merge credentials: api_creds
|
27
|
-
params
|
27
|
+
params[:#{key}] = id
|
28
28
|
#{klass}.find(aid, params)
|
29
29
|
end
|
30
30
|
M2
|
@@ -33,7 +33,7 @@ module NCore
|
|
33
33
|
def create_#{assoc.singularize}(params={})
|
34
34
|
raise UnsavedObjectError unless id
|
35
35
|
params = parse_request_params(params).reverse_merge credentials: api_creds
|
36
|
-
params
|
36
|
+
params[:#{key}] = id
|
37
37
|
#{klass}.create(params)
|
38
38
|
end
|
39
39
|
M3
|
@@ -42,7 +42,7 @@ module NCore
|
|
42
42
|
def update_#{assoc.singularize}(aid, params={})
|
43
43
|
raise UnsavedObjectError unless id
|
44
44
|
params = parse_request_params(params).reverse_merge credentials: api_creds
|
45
|
-
params
|
45
|
+
params[:#{key}] = id
|
46
46
|
#{klass}.update(aid, params)
|
47
47
|
end
|
48
48
|
M4
|
@@ -50,7 +50,7 @@ module NCore
|
|
50
50
|
def create_#{assoc.singularize}!(params={})
|
51
51
|
raise UnsavedObjectError unless id
|
52
52
|
params = parse_request_params(params).reverse_merge credentials: api_creds
|
53
|
-
params
|
53
|
+
params[:#{key}] = id
|
54
54
|
#{klass}.create!(params)
|
55
55
|
end
|
56
56
|
M5
|
@@ -58,7 +58,7 @@ module NCore
|
|
58
58
|
def update_#{assoc.singularize}!(aid, params={})
|
59
59
|
raise UnsavedObjectError unless id
|
60
60
|
params = parse_request_params(params).reverse_merge credentials: api_creds
|
61
|
-
params
|
61
|
+
params[:#{key}] = id
|
62
62
|
#{klass}.update!(aid, params)
|
63
63
|
end
|
64
64
|
M6
|
@@ -67,7 +67,7 @@ module NCore
|
|
67
67
|
def delete_#{assoc.singularize}(aid, params={})
|
68
68
|
raise UnsavedObjectError unless id
|
69
69
|
params = parse_request_params(params).reverse_merge credentials: api_creds
|
70
|
-
params
|
70
|
+
params[:#{key}] = id
|
71
71
|
#{klass}.delete(aid, params)
|
72
72
|
end
|
73
73
|
M7
|
@@ -75,15 +75,15 @@ module NCore
|
|
75
75
|
def delete_#{assoc.singularize}!(aid, params={})
|
76
76
|
raise UnsavedObjectError unless id
|
77
77
|
params = parse_request_params(params).reverse_merge credentials: api_creds
|
78
|
-
params
|
78
|
+
params[:#{key}] = id
|
79
79
|
#{klass}.delete!(aid, params)
|
80
80
|
end
|
81
81
|
M8
|
82
82
|
end
|
83
83
|
|
84
|
-
def belongs_to(assoc,
|
84
|
+
def belongs_to(assoc, class_name: nil)
|
85
85
|
assoc = assoc.to_s
|
86
|
-
klass
|
86
|
+
klass = class_name || "#{module_name}::#{assoc.camelize}"
|
87
87
|
class_eval <<-M1, __FILE__, __LINE__+1
|
88
88
|
attr :#{assoc}_id
|
89
89
|
def #{assoc}(params={})
|
data/lib/ncore/attributes.rb
CHANGED
@@ -10,6 +10,7 @@ module NCore
|
|
10
10
|
module ClassMethods
|
11
11
|
def attr(*attrs)
|
12
12
|
attrs.each do |attr|
|
13
|
+
check_existing_method(attr)
|
13
14
|
class_eval <<-AR, __FILE__, __LINE__+1
|
14
15
|
def #{attr}
|
15
16
|
self[:#{attr}]
|
@@ -20,6 +21,7 @@ module NCore
|
|
20
21
|
|
21
22
|
def attr_datetime(*attrs)
|
22
23
|
attrs.each do |attr|
|
24
|
+
check_existing_method(attr)
|
23
25
|
class_eval <<-AD, __FILE__, __LINE__+1
|
24
26
|
def #{attr}
|
25
27
|
case self[:#{attr}]
|
@@ -39,6 +41,7 @@ module NCore
|
|
39
41
|
|
40
42
|
def attr_decimal(*attrs)
|
41
43
|
attrs.each do |attr|
|
44
|
+
check_existing_method(attr)
|
42
45
|
class_eval <<-AD, __FILE__, __LINE__+1
|
43
46
|
def #{attr}
|
44
47
|
case self[:#{attr}]
|
@@ -52,10 +55,19 @@ module NCore
|
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
58
|
+
def check_existing_method(attr)
|
59
|
+
if method_defined?(attr) || private_method_defined?(attr)
|
60
|
+
sc = self
|
61
|
+
sc = sc.superclass while sc.superclass != Object
|
62
|
+
warn "Warning: Existing method #{sc.name}##{attr} being overwritten at #{caller[3]}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
55
66
|
def parse_request_params(params={}, opts={})
|
56
67
|
params = params.with_indifferent_access
|
57
68
|
req = params.delete(:request)
|
58
69
|
creds = params.delete(:credentials)
|
70
|
+
cache = params.delete(:cache)
|
59
71
|
if opts[:json_root]
|
60
72
|
if params.key?(opts[:json_root])
|
61
73
|
o = params
|
@@ -67,6 +79,7 @@ module NCore
|
|
67
79
|
end
|
68
80
|
o[:request] = req if req
|
69
81
|
o[:credentials] = creds if creds
|
82
|
+
o[:cache] = cache if cache
|
70
83
|
o
|
71
84
|
end
|
72
85
|
end
|
@@ -86,23 +99,18 @@ module NCore
|
|
86
99
|
creds_attr = attribs.delete(:credentials)
|
87
100
|
@api_creds = api_creds || creds_attr
|
88
101
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
errors: attribs.delete(:errors),
|
95
|
-
data: attribs.delete(:data) || attribs
|
96
|
-
}
|
97
|
-
end
|
98
|
-
load(load_attrs)
|
102
|
+
load(
|
103
|
+
metadata: attribs.delete(:metadata),
|
104
|
+
errors: attribs.delete(:errors),
|
105
|
+
data: attribs.delete(:data) || attribs
|
106
|
+
)
|
99
107
|
end
|
100
108
|
|
101
109
|
|
102
110
|
def attributes
|
103
111
|
Util.deep_clone(@attribs)
|
104
112
|
end
|
105
|
-
|
113
|
+
alias_method :as_json, :attributes
|
106
114
|
|
107
115
|
def [](attr)
|
108
116
|
@attribs[attr]
|
@@ -150,10 +158,10 @@ module NCore
|
|
150
158
|
end
|
151
159
|
|
152
160
|
|
153
|
-
def load(
|
154
|
-
self.metadata =
|
155
|
-
self.errors =
|
156
|
-
|
161
|
+
def load(data:, errors: nil, metadata: nil)
|
162
|
+
self.metadata = metadata || {}.with_indifferent_access
|
163
|
+
self.errors = parse_errors(errors)
|
164
|
+
data.each do |k,v|
|
157
165
|
if respond_to? "#{k}="
|
158
166
|
send "#{k}=", self.class.interpret_type(v, api_creds)
|
159
167
|
else
|
@@ -163,6 +171,17 @@ module NCore
|
|
163
171
|
self
|
164
172
|
end
|
165
173
|
|
174
|
+
def parse_errors(errors)
|
175
|
+
errors ||= []
|
176
|
+
if errors.is_a?(::ActiveModel::Errors)
|
177
|
+
errors
|
178
|
+
else
|
179
|
+
::ActiveModel::Errors.new(self).tap do |e0|
|
180
|
+
errors.each{|msg| e0.add :base, msg }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
166
185
|
end
|
167
186
|
|
168
187
|
|
data/lib/ncore/base.rb
CHANGED
@@ -4,8 +4,10 @@ module NCore
|
|
4
4
|
|
5
5
|
included do
|
6
6
|
extend Associations
|
7
|
+
include ActiveModel
|
7
8
|
include Attributes
|
8
9
|
include Client
|
10
|
+
include Client::Cache
|
9
11
|
include Identity
|
10
12
|
include Lifecycle
|
11
13
|
include Util
|
@@ -23,13 +25,13 @@ module NCore
|
|
23
25
|
include Update if types.include? :update
|
24
26
|
end
|
25
27
|
|
26
|
-
def
|
28
|
+
def resource_path
|
27
29
|
class_name.underscore.pluralize
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
|
-
def
|
32
|
-
"#{self.class.
|
33
|
+
def resource_path
|
34
|
+
"#{self.class.resource_path}/#{CGI.escape((id||'-').to_s)}"
|
33
35
|
end
|
34
36
|
|
35
37
|
end
|
data/lib/ncore/client.rb
CHANGED
@@ -6,11 +6,12 @@ module NCore
|
|
6
6
|
|
7
7
|
module ClassMethods
|
8
8
|
|
9
|
-
# opts - {params: {}, headers: {}, credentials: {}}
|
9
|
+
# opts - {params: {}, headers: {}, credentials: {}, cache: {}}
|
10
10
|
# unknown keys assumed to be :params if :params is missing
|
11
11
|
def request(method, url, opts={})
|
12
12
|
opts = opts.with_indifferent_access
|
13
13
|
request_credentials = opts.delete 'credentials'
|
14
|
+
cache_opts = opts.delete 'cache'
|
14
15
|
headers = opts.delete('headers') || {}
|
15
16
|
params = opts['params'] || opts
|
16
17
|
|
@@ -48,7 +49,7 @@ module NCore
|
|
48
49
|
write_timeout: 50,
|
49
50
|
}
|
50
51
|
|
51
|
-
response = execute_request(rest_opts)
|
52
|
+
response = execute_request(rest_opts, cache_opts)
|
52
53
|
parsed = parse_response(response)
|
53
54
|
[parsed, request_credentials]
|
54
55
|
end
|
@@ -58,7 +59,7 @@ module NCore
|
|
58
59
|
|
59
60
|
def retrieve_credentials
|
60
61
|
if credentials.blank?
|
61
|
-
raise
|
62
|
+
raise module_parent::Error, credentials_error_message
|
62
63
|
end
|
63
64
|
credentials
|
64
65
|
end
|
@@ -69,7 +70,7 @@ module NCore
|
|
69
70
|
|
70
71
|
def retrieve_default_url
|
71
72
|
if default_url.blank?
|
72
|
-
raise
|
73
|
+
raise module_parent::Error, credentials_error_message
|
73
74
|
end
|
74
75
|
default_url
|
75
76
|
end
|
@@ -87,12 +88,12 @@ module NCore
|
|
87
88
|
|
88
89
|
def build_query_string(params)
|
89
90
|
if params.any?
|
90
|
-
query_string = params.map do |k,v|
|
91
|
+
query_string = params.sort.map do |k,v|
|
91
92
|
if v.is_a?(Array)
|
92
93
|
if v.empty?
|
93
94
|
"#{k.to_s}[]="
|
94
95
|
else
|
95
|
-
v.map do |v2|
|
96
|
+
v.sort.map do |v2|
|
96
97
|
"#{k.to_s}[]=#{CGI::escape(v2.to_s)}"
|
97
98
|
end.join('&')
|
98
99
|
end
|
@@ -163,7 +164,7 @@ module NCore
|
|
163
164
|
end
|
164
165
|
|
165
166
|
|
166
|
-
def execute_request(rest_opts)
|
167
|
+
def execute_request(rest_opts, _)
|
167
168
|
debug_request rest_opts if debug
|
168
169
|
|
169
170
|
tries = 0
|
@@ -176,7 +177,7 @@ module NCore
|
|
176
177
|
tries += 1
|
177
178
|
response = connection.request rest_opts.except(:url)
|
178
179
|
rescue Excon::Error::Socket, Excon::Errors::SocketError, Excon::Error::Timeout,
|
179
|
-
Errno::EADDRNOTAVAIL
|
180
|
+
Errno::EADDRNOTAVAIL => e
|
180
181
|
# retry when keepalive was closed
|
181
182
|
if tries <= 1 #&& e.message =~ /end of file reached/
|
182
183
|
retry
|
@@ -188,30 +189,30 @@ module NCore
|
|
188
189
|
debug_response response if debug
|
189
190
|
end
|
190
191
|
rescue Errno::ECONNRESET
|
191
|
-
raise
|
192
|
+
raise module_parent::ConnectionError, "Connection reset for #{host_for_error rest_opts[:url]} : check network or visit #{status_page}."
|
192
193
|
rescue Errno::ECONNREFUSED
|
193
|
-
raise
|
194
|
+
raise module_parent::ConnectionError, "Connection error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
|
194
195
|
rescue Excon::Error::Timeout => e
|
195
196
|
case e.message
|
196
197
|
when /timeout reached/
|
197
|
-
raise
|
198
|
+
raise module_parent::ConnectionError, "Connection error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
|
198
199
|
else
|
199
200
|
raise e
|
200
201
|
end
|
201
202
|
rescue Excon::Errors::SocketError => e
|
202
203
|
case e.message
|
203
204
|
when /Unable to verify certificate/
|
204
|
-
raise
|
205
|
+
raise module_parent::ConnectionError, "Unable to verify certificate for #{host_for_error rest_opts[:url]} : verify URL or disable SSL certificate verification (insecure)."
|
205
206
|
when /Name or service not known/, /No address associated with hostname/
|
206
|
-
raise
|
207
|
+
raise module_parent::ConnectionError, "DNS error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
|
207
208
|
when /Errno::ECONNREFUSED/
|
208
|
-
raise
|
209
|
+
raise module_parent::ConnectionError, "Connection error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
|
209
210
|
else
|
210
211
|
raise e
|
211
212
|
end
|
212
213
|
rescue SocketError => e
|
213
214
|
if e.message =~ /nodename nor servname provided/
|
214
|
-
raise
|
215
|
+
raise module_parent::ConnectionError, "DNS error for #{host_for_error rest_opts[:url]} : check network and DNS or visit #{status_page}."
|
215
216
|
else
|
216
217
|
raise e
|
217
218
|
end
|
@@ -219,21 +220,21 @@ module NCore
|
|
219
220
|
|
220
221
|
case response.status
|
221
222
|
when 401 # API auth valid; API call itself is an auth-related call and failed
|
222
|
-
raise
|
223
|
+
raise module_parent::AuthenticationFailed
|
223
224
|
when 402
|
224
|
-
raise
|
225
|
+
raise module_parent::AccountInactive, "Account inactive; login to portal to check account status."
|
225
226
|
when 403 # API auth failed or insufficient permissions
|
226
|
-
raise
|
227
|
+
raise module_parent::AccessDenied, "Access denied; check your API credentials and permissions."
|
227
228
|
when 404
|
228
|
-
raise
|
229
|
+
raise module_parent::RecordNotFound
|
229
230
|
when 409, 422
|
230
231
|
# pass through
|
231
232
|
when 429
|
232
|
-
raise
|
233
|
+
raise module_parent::RateLimited
|
233
234
|
when 400..499
|
234
|
-
raise
|
235
|
+
raise module_parent::Error, "Client error: #{response.status}\n #{response.body}"
|
235
236
|
when 500..599
|
236
|
-
raise
|
237
|
+
raise module_parent::Error, "Server error: #{response.status}\n #{response.body}"
|
237
238
|
end
|
238
239
|
response
|
239
240
|
end
|
@@ -246,20 +247,20 @@ module NCore
|
|
246
247
|
begin
|
247
248
|
json = MultiJson.load(response.body, symbolize_keys: false) || {}
|
248
249
|
rescue MultiJson::ParseError
|
249
|
-
raise
|
250
|
+
raise module_parent::Error, "Unable to parse API response; HTTP status: #{response.status}; body: #{response.body.inspect}"
|
250
251
|
end
|
251
252
|
else
|
252
253
|
begin
|
253
254
|
json = JSON.parse(response.body, symbolize_names: false) || {}
|
254
255
|
rescue JSON::ParserError
|
255
|
-
raise
|
256
|
+
raise module_parent::Error, "Unable to parse API response; HTTP status: #{response.status}; body: #{response.body.inspect}"
|
256
257
|
end
|
257
258
|
end
|
258
259
|
end
|
259
260
|
json = json.with_indifferent_access
|
260
261
|
errors = json.delete(:errors) || []
|
261
262
|
if errors.any?
|
262
|
-
errors = errors.values.flatten
|
263
|
+
errors = errors.values.flatten if errors.is_a?(Hash)
|
263
264
|
metadata, json = json, {}
|
264
265
|
else
|
265
266
|
errors = []
|
@@ -296,16 +297,17 @@ module NCore
|
|
296
297
|
|
297
298
|
def verify_ssl_cert?
|
298
299
|
return @verify_ssl_cert unless @verify_ssl_cert.nil?
|
299
|
-
|
300
|
-
|
301
|
-
bundle_readable = File.readable?(ssl_cert_bundle) rescue false
|
302
|
-
unless bundle_readable
|
303
|
-
raise parent::CertificateError, "Unable to read SSL cert bundle #{ssl_cert_bundle}."
|
304
|
-
end
|
305
|
-
end
|
300
|
+
bundle_readable = File.readable?(ssl_cert_bundle)
|
301
|
+
if verify_ssl && bundle_readable
|
306
302
|
@verify_ssl_cert = true
|
307
303
|
else
|
308
|
-
m =
|
304
|
+
m = 'WARNNG: SSL cert verification is disabled.'
|
305
|
+
unless verify_ssl
|
306
|
+
m += " Enable verification with: #{module_parent}::Api.verify_ssl = true."
|
307
|
+
end
|
308
|
+
unless bundle_readable
|
309
|
+
m += " Unable to read CA bundle #{ssl_cert_bundle}."
|
310
|
+
end
|
309
311
|
$stderr.puts m
|
310
312
|
@verify_ssl_cert = false
|
311
313
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module NCore
|
2
|
+
module Client::Cache
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
# cache_opts: true
|
10
|
+
# use *::Api.cache_store
|
11
|
+
# cache_opts: {...}
|
12
|
+
# use: *::Api.cache_store, with options: {...}
|
13
|
+
# hint: add force: true execute the query and rewrite the cache
|
14
|
+
# cache_opts: Store.new
|
15
|
+
# use Store.new as-is
|
16
|
+
def execute_request(req, cache_opts=nil)
|
17
|
+
case cache_opts
|
18
|
+
when true
|
19
|
+
store, cache_opts = cache_store, {}
|
20
|
+
when Hash
|
21
|
+
store, cache_opts = cache_store, cache_opts.symbolize_keys
|
22
|
+
when nil, false
|
23
|
+
store = false
|
24
|
+
else
|
25
|
+
store, cache_opts = cache_opts, {}
|
26
|
+
end
|
27
|
+
|
28
|
+
if store && req[:method] == :get
|
29
|
+
store.fetch request_cache_key(req.slice(:url, :headers)), cache_opts do
|
30
|
+
super
|
31
|
+
end
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def request_cache_key(url:, headers:)
|
39
|
+
[ 'ncore',
|
40
|
+
url.gsub(/[^a-zA-Z0-9]+/,'-'),
|
41
|
+
Digest::MD5.hexdigest(headers.sort.to_s)
|
42
|
+
].join ':'
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
data/lib/ncore/configuration.rb
CHANGED
@@ -28,6 +28,9 @@ module NCore
|
|
28
28
|
mattr_accessor :strict_attributes
|
29
29
|
self.strict_attributes = true
|
30
30
|
|
31
|
+
mattr_accessor :i18n_scope
|
32
|
+
self.i18n_scope = :ncore
|
33
|
+
|
31
34
|
mattr_accessor :instrument_key
|
32
35
|
self.instrument_key = 'request.ncore'
|
33
36
|
|
@@ -37,42 +40,31 @@ module NCore
|
|
37
40
|
mattr_accessor :auth_header_prefix
|
38
41
|
self.auth_header_prefix = 'X-Api'
|
39
42
|
|
40
|
-
|
43
|
+
mattr_reader :bearer_credential_key
|
44
|
+
class_eval <<-MTH
|
45
|
+
def self.bearer_credential_key=(v)
|
46
|
+
@@bearer_credential_key = v&.to_s
|
47
|
+
end
|
48
|
+
def bearer_credential_key=(v)
|
49
|
+
@@bearer_credential_key = v&.to_s
|
50
|
+
end
|
51
|
+
MTH
|
41
52
|
|
42
53
|
mattr_accessor :credentials_error_message
|
43
|
-
self.credentials_error_message = %Q{Missing API credentials. Set default credentials using "#{self.
|
54
|
+
self.credentials_error_message = %Q{Missing API credentials. Set default credentials using "#{self.module_parent.name}.credentials = {api_user: YOUR_API_USER, api_key: YOUR_API_KEY}"}
|
44
55
|
|
45
56
|
mattr_accessor :verify_ssl
|
46
57
|
self.verify_ssl = true
|
47
58
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
@@ssl_cert_bundle = v
|
53
|
-
end
|
54
|
-
def ssl_cert_bundle=(v)
|
55
|
-
v = find_excon_bundle if v==:bundled
|
56
|
-
@@ssl_cert_bundle = v
|
57
|
-
end
|
58
|
-
MTH
|
59
|
+
mattr_accessor :ssl_cert_bundle
|
60
|
+
self.ssl_cert_bundle = File.dirname(__FILE__)+'/ssl/ca-certificates.crt'
|
61
|
+
|
62
|
+
mattr_accessor :cache_store
|
59
63
|
|
60
64
|
mattr_accessor :logger
|
61
65
|
self.logger = Logger.new(STDOUT)
|
62
66
|
end
|
63
67
|
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def find_excon_bundle
|
68
|
-
b = Gem.find_files_from_load_path('../data/cacert.pem').select{|p| p=~/excon/}.first
|
69
|
-
if b
|
70
|
-
b.freeze
|
71
|
-
else
|
72
|
-
raise parent::CertificateError, 'Failed to locate CA cert bundle from excon. Specify a full path instead.'
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
68
|
end
|
77
69
|
|
78
70
|
end
|