ecoportal-api 0.8.5 → 0.9.2
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/.gitignore +20 -20
- data/.rspec +3 -3
- data/.rubocop.yml +55 -55
- data/.travis.yml +5 -5
- data/.yardopts +10 -10
- data/CHANGELOG.md +257 -236
- data/Gemfile +6 -6
- data/LICENSE +21 -21
- data/README.md +34 -34
- data/Rakefile +27 -27
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/ecoportal-api.gemspec +36 -36
- data/lib/ecoportal/api/common/base_class.rb +33 -29
- data/lib/ecoportal/api/common/base_model.rb +195 -177
- data/lib/ecoportal/api/common/batch_operation.rb +119 -119
- data/lib/ecoportal/api/common/batch_response.rb +34 -34
- data/lib/ecoportal/api/common/client.rb +198 -196
- data/lib/ecoportal/api/common/doc_helpers.rb +29 -29
- data/lib/ecoportal/api/common/elastic_apm_integration.rb +112 -112
- data/lib/ecoportal/api/common/hash_diff.rb +41 -41
- data/lib/ecoportal/api/common/logging.rb +12 -12
- data/lib/ecoportal/api/common/response.rb +31 -31
- data/lib/ecoportal/api/common/wrapped_response.rb +54 -54
- data/lib/ecoportal/api/common.rb +18 -18
- data/lib/ecoportal/api/errors/base.rb +8 -8
- data/lib/ecoportal/api/errors/time_out.rb +8 -8
- data/lib/ecoportal/api/errors.rb +9 -9
- data/lib/ecoportal/api/internal/account.rb +99 -100
- data/lib/ecoportal/api/internal/login_provider.rb +9 -9
- data/lib/ecoportal/api/internal/login_providers.rb +33 -33
- data/lib/ecoportal/api/internal/people.rb +14 -14
- data/lib/ecoportal/api/internal/permissions.rb +14 -13
- data/lib/ecoportal/api/internal/person.rb +101 -53
- data/lib/ecoportal/api/internal/person_details.rb +9 -9
- data/lib/ecoportal/api/internal/person_schema.rb +10 -10
- data/lib/ecoportal/api/internal/person_schemas.rb +11 -11
- data/lib/ecoportal/api/internal/policy_group.rb +9 -9
- data/lib/ecoportal/api/internal/policy_groups.rb +32 -32
- data/lib/ecoportal/api/internal/preferences.rb +31 -31
- data/lib/ecoportal/api/internal/schema_field.rb +8 -8
- data/lib/ecoportal/api/internal/schema_field_value.rb +8 -8
- data/lib/ecoportal/api/internal.rb +31 -31
- data/lib/ecoportal/api/logger.rb +62 -62
- data/lib/ecoportal/api/v1/people.rb +218 -218
- data/lib/ecoportal/api/v1/person.rb +138 -135
- data/lib/ecoportal/api/v1/person_details.rb +94 -82
- data/lib/ecoportal/api/v1/person_schema.rb +53 -53
- data/lib/ecoportal/api/v1/person_schemas.rb +48 -48
- data/lib/ecoportal/api/v1/schema_field.rb +34 -34
- data/lib/ecoportal/api/v1/schema_field_value.rb +65 -65
- data/lib/ecoportal/api/v1.rb +49 -49
- data/lib/ecoportal/api/version.rb +5 -5
- data/lib/ecoportal/api.rb +16 -16
- metadata +3 -3
@@ -1,196 +1,198 @@
|
|
1
|
-
require 'http'
|
2
|
-
module Ecoportal
|
3
|
-
module API
|
4
|
-
module Common
|
5
|
-
# @note
|
6
|
-
# - You can see the documentation of the `HTTP` module in [the repository](https://github.com/httprb/http)
|
7
|
-
# - it does `extend` the module `Chainable` ([chainable.rb](https://github.com/httprb/http/blob/master/lib/http/chainable.rb)),
|
8
|
-
# - where all the http requests are dev by using `HTTP::Client#request` ([client.rb](https://github.com/httprb/http/blob/master/lib/http/client.rb))
|
9
|
-
# - which calls `build_request` (new `HTTP::Request`) and `perform` (new `HTTP::Connection`)
|
10
|
-
# - to return `HTTP::Response` ([response.rb](https://github.com/httprb/http/blob/master/lib/http/response.rb))
|
11
|
-
# @attr_reader logger [Logger] the logger.
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# @
|
21
|
-
# @param
|
22
|
-
# @param
|
23
|
-
# @param
|
24
|
-
# @
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@
|
29
|
-
@
|
30
|
-
@
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
@base_uri = "
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
#
|
45
|
-
#
|
46
|
-
# log(:
|
47
|
-
# log(:
|
48
|
-
#
|
49
|
-
#
|
50
|
-
# @
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
#
|
58
|
-
#
|
59
|
-
# @
|
60
|
-
# @
|
61
|
-
# @option params [String] :
|
62
|
-
# @
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
#
|
74
|
-
#
|
75
|
-
# @
|
76
|
-
# @
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
#
|
88
|
-
#
|
89
|
-
# @
|
90
|
-
# @
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
#
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
#
|
113
|
-
#
|
114
|
-
# @
|
115
|
-
# @
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
#
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
#
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
HTTP.headers("X-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
#
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
end
|
1
|
+
require 'http'
|
2
|
+
module Ecoportal
|
3
|
+
module API
|
4
|
+
module Common
|
5
|
+
# @note
|
6
|
+
# - You can see the documentation of the `HTTP` module in [the repository](https://github.com/httprb/http)
|
7
|
+
# - it does `extend` the module `Chainable` ([chainable.rb](https://github.com/httprb/http/blob/master/lib/http/chainable.rb)),
|
8
|
+
# - where all the http requests are dev by using `HTTP::Client#request` ([client.rb](https://github.com/httprb/http/blob/master/lib/http/client.rb))
|
9
|
+
# - which calls `build_request` (new `HTTP::Request`) and `perform` (new `HTTP::Connection`)
|
10
|
+
# - to return `HTTP::Response` ([response.rb](https://github.com/httprb/http/blob/master/lib/http/response.rb))
|
11
|
+
# @attr_reader logger [Logger] the logger.
|
12
|
+
# @attr_reader host [String] the remote target server.
|
13
|
+
class Client
|
14
|
+
include Common::ElasticApmIntegration
|
15
|
+
DELAY_REQUEST_RETRY = 5
|
16
|
+
|
17
|
+
attr_accessor :logger
|
18
|
+
attr_reader :host
|
19
|
+
|
20
|
+
# @note the `api_key` will be automatically added as parameter `X-ApiKey` in the header of the http requests.
|
21
|
+
# @param api_key [String] the key version to stablish the api connection.
|
22
|
+
# @param version [String] it is part of the base url and will determine the api version we query against.
|
23
|
+
# @param host [String] api server domain.
|
24
|
+
# @param logger [Logger] an object with `Logger` interface to generate logs.
|
25
|
+
# @param response_logging [Boolean] whether or not batch responses should be logged
|
26
|
+
# @return [Client] an object that holds the configuration of the api connection.
|
27
|
+
def initialize(api_key:, version: "v1", host: "live.ecoportal.com", logger: nil, response_logging: false)
|
28
|
+
@version = version
|
29
|
+
@api_key = api_key
|
30
|
+
@logger = logger
|
31
|
+
@host = host
|
32
|
+
@response_logging_enabled = response_logging
|
33
|
+
if host.match(/^localhost|^127\.0\.0\.1/)
|
34
|
+
@base_uri = "http://#{host}/api/"
|
35
|
+
else
|
36
|
+
@base_uri = "https://#{host}/api/"
|
37
|
+
end
|
38
|
+
log(:info) { "#{version} client initialized pointing at #{host}" }
|
39
|
+
if @api_key.nil? || @api_key.match(/\A\W*\z/)
|
40
|
+
log(:error) { "Api-key missing!" }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Logger interface.
|
45
|
+
# @example:
|
46
|
+
# log(:info) {"General information on what's going on"}
|
47
|
+
# log(:warn) {"This is a warning that something is likely to have gone amiss"}
|
48
|
+
# log(:error) {"Something went wrong"}
|
49
|
+
# log(:fatal) {"An unrecoverable error has happend"}
|
50
|
+
# @param level [Symbol] the level that the message should be logged.
|
51
|
+
# @yield [] generates the message.
|
52
|
+
# @yieldreturn [String] the generated message.
|
53
|
+
def log(level, &block)
|
54
|
+
logger.send(level, &block) if logger
|
55
|
+
end
|
56
|
+
|
57
|
+
# Sends an http `GET` request against the api version using `path` to complete the base url,
|
58
|
+
# and adding the key_value pairs of `params` in the http _header_.
|
59
|
+
# @param path [String] the tail that completes the url of the request.
|
60
|
+
# @param params [Hash] the header paramters of the http request (not including the api key).
|
61
|
+
# @option params [String] :page the current page we are requesting with given the `:per_page` offset.
|
62
|
+
# @option params [String] :per_page the offset or the number of entries you get per request.
|
63
|
+
# @option params [String] :q some text to search. Omit this parameter to target all the entries.
|
64
|
+
# @return [Common::Reponse] the basic custom response object.
|
65
|
+
def get(path, params: {})
|
66
|
+
instrument("GET", path, params) do
|
67
|
+
request do |http|
|
68
|
+
http.get(url_for(path), params: params)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sends an http `POST` request against the api version using `path` to complete the base url,
|
74
|
+
# and the `data` as a body of the http request.
|
75
|
+
# @note it automatically adds the http header param `Content-Type` as `application/json`
|
76
|
+
# @param path [String] the tail that completes the url of the request.
|
77
|
+
# @param data [String] the body of the query in json format.
|
78
|
+
# @return [Common::Reponse] the basic custom response object.
|
79
|
+
def post(path, data:)
|
80
|
+
instrument("POST", path, data) do
|
81
|
+
request do |http|
|
82
|
+
http.post(url_for(path), json: data)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Sends an http `PATCH` request against the api version using `path` to complete the base url,
|
88
|
+
# and the `data` as a body of the http request.
|
89
|
+
# @note it automatically adds the http header param `Content-Type` as `application/json`
|
90
|
+
# @param path [String] the tail that completes the url of the request.
|
91
|
+
# @param data [String] the body of the query in json format.
|
92
|
+
# @return [Common::Reponse] the basic custom response object.
|
93
|
+
def patch(path, data:)
|
94
|
+
instrument("PATCH", path, data) do
|
95
|
+
request do |http|
|
96
|
+
http.patch(url_for(path), json: data)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Sends an http `DELETE` request against the api version using `path` to complete the base url.
|
102
|
+
# @param path [String] the tail that completes the url of the request.
|
103
|
+
# @return [Common::Reponse] the basic custom response object.
|
104
|
+
def delete(path)
|
105
|
+
instrument("DELETE", path) do
|
106
|
+
request do |http|
|
107
|
+
http.delete(url_for(path))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Allows to launch a different operation via `block`, providing the
|
113
|
+
# basic HTTP connection to the block.
|
114
|
+
# @yield [http] launch specific http request.
|
115
|
+
# @yieldparam http [HTTP] the http connection.
|
116
|
+
# @yieldreturn [Common::Response] the basic custom response object.
|
117
|
+
# @return [Common::Reponse] the basic custom response object.
|
118
|
+
def request
|
119
|
+
wrap_response yield(base_request)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Wrap with basic custom object of the gem for responses.
|
123
|
+
# @param response [HTTP::Response]
|
124
|
+
# @return [Common::Reponse] the basic custom response object.
|
125
|
+
def wrap_response(response)
|
126
|
+
Ecoportal::API::Common::Response.new(response)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Creates a HTTP object adding the `X-ApiKey` or `X-ECOPORTAL-API-KEY` param to the header, depending on the API version.
|
130
|
+
# @note It configures HTTP so it only allows body data in json format.
|
131
|
+
# @return [HTTP] HTTP object.
|
132
|
+
def base_request
|
133
|
+
@base_request ||= begin
|
134
|
+
case @version
|
135
|
+
when "v2"
|
136
|
+
HTTP.headers("X-ECOPORTAL-API-KEY" => @api_key).accept(:json)
|
137
|
+
else
|
138
|
+
HTTP.headers("X-ApiKey" => @api_key).accept(:json)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Full URl builder of the request
|
144
|
+
# @param path [String] the tail that completes the url of the request.
|
145
|
+
# @return [String] the final url.
|
146
|
+
def url_for(path)
|
147
|
+
@base_uri+@version+path
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def instrument(method, path, data = nil, &block)
|
153
|
+
raise "Expected block" unless block
|
154
|
+
start_time = Time.now.to_f
|
155
|
+
log(:info) { "#{method} #{url_for(path)}" }
|
156
|
+
log(:debug) { "Data: #{JSON.pretty_generate(data)}" }
|
157
|
+
|
158
|
+
with_retry(&block).tap do |result|
|
159
|
+
end_time = Time.now.to_f
|
160
|
+
log(result.success?? :info : :warn) do
|
161
|
+
"Took %.2fs, Status #{result.status}" % (end_time - start_time)
|
162
|
+
end
|
163
|
+
log(result.success?? :debug : :warn) do
|
164
|
+
"Response: #{JSON.pretty_generate(result.body)}"
|
165
|
+
end if @response_logging_enabled
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Helper to ensure unexpected server errors do not bring client scripts immediately down
|
170
|
+
def with_retry(attempts = 3, delay = DELAY_REQUEST_RETRY, error_safe: true, &block)
|
171
|
+
response = nil
|
172
|
+
attempts.times do |i|
|
173
|
+
remaining = attempts - i - 1
|
174
|
+
begin
|
175
|
+
response = block.call
|
176
|
+
rescue HTTP::ConnectionError => e
|
177
|
+
raise unless error_safe && remaining > 0
|
178
|
+
log(:error) { "Got connection error: #{e.message}" }
|
179
|
+
response = with_retry(remaining, error_safe: error_safe, &block)
|
180
|
+
rescue IOError => e
|
181
|
+
raise unless error_safe && remaining > 0
|
182
|
+
log(:error) { "Got IO error: #{e.message}" }
|
183
|
+
response = with_retry(remaining, error_safe: error_safe, &block)
|
184
|
+
end
|
185
|
+
return response unless unexpected_server_error?(response.status)
|
186
|
+
log_unexpected_server_error(response)
|
187
|
+
msg = "Got server error (#{response.status}): #{response.body}\n"
|
188
|
+
msg += "Going to retry (#{i} out of #{attempts})"
|
189
|
+
log(:error) { msg }
|
190
|
+
sleep(delay) if i < attempts
|
191
|
+
end
|
192
|
+
response
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -1,29 +1,29 @@
|
|
1
|
-
module Ecoportal
|
2
|
-
module API
|
3
|
-
module Common
|
4
|
-
module DocHelpers
|
5
|
-
|
6
|
-
def get_body(doc)
|
7
|
-
if doc.respond_to?(:as_update)
|
8
|
-
doc.as_update
|
9
|
-
elsif doc.respond_to?(:as_json)
|
10
|
-
doc.as_json
|
11
|
-
else
|
12
|
-
doc
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def get_id(doc)
|
17
|
-
id = nil
|
18
|
-
id ||= doc.id if doc.respond_to?(:id)
|
19
|
-
id ||= doc.external_id if doc.respond_to?(:external_id)
|
20
|
-
id ||= doc["id"] if doc.is_a?(Hash)
|
21
|
-
id ||= doc["external_id"] if doc.is_a?(Hash)
|
22
|
-
id ||= doc if doc.is_a?(String)
|
23
|
-
id or raise "No ID has been given!"
|
24
|
-
id
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
1
|
+
module Ecoportal
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module DocHelpers
|
5
|
+
|
6
|
+
def get_body(doc)
|
7
|
+
if doc.respond_to?(:as_update)
|
8
|
+
doc.as_update
|
9
|
+
elsif doc.respond_to?(:as_json)
|
10
|
+
doc.as_json
|
11
|
+
else
|
12
|
+
doc
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_id(doc)
|
17
|
+
id = nil
|
18
|
+
id ||= doc.id if doc.respond_to?(:id)
|
19
|
+
id ||= doc.external_id if doc.respond_to?(:external_id)
|
20
|
+
id ||= doc["id"] if doc.is_a?(Hash)
|
21
|
+
id ||= doc["external_id"] if doc.is_a?(Hash)
|
22
|
+
id ||= doc if doc.is_a?(String)
|
23
|
+
id or raise "No ID has been given!"
|
24
|
+
id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|