ncore 2.3.3 → 3.0.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/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
|