ecoportal-api 0.7.4 → 0.8.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 275ffcb5e9bfe17f44295a7c4438b9e169dc87f22afe19591752425ef008cdba
4
- data.tar.gz: aeb53e930f75dc7299fc400a8a00624428dfe496b77a5fdf1af1075b144a6944
3
+ metadata.gz: dc651f2474f74b8f767c9df5b4611bd8adefdff75f8e25365135c89561229738
4
+ data.tar.gz: cb7a8ff5c0c03ba7e371f987d6b277c001b33e57716ab6cf03628d28bac53b61
5
5
  SHA512:
6
- metadata.gz: 2e2e4c03089ccf675057b335a9c9e46a5a40a92d3720b256fb9a679eec6431885889171960bf106e3586efbc77d9b2384262c9e4aa52cdf34b6c291be78b76dd
7
- data.tar.gz: c1df25f369172d215ba64bf932b63d78514871579540edabc991719a737999a5700828352bceca140d5715861b47cb6b2e4e56fab4b7b916fe5ef42627ec7e4f
6
+ metadata.gz: f89285565edeaa9cbe4a98970bae8602a943c7c865121a89782f512353a8be74529578eecb4f48e893f546f172425dd38af0759412f53b8fc3a91989d6dbe52a
7
+ data.tar.gz: 4f51e3e6d080745261c033acf511fdd341ebf9f97003f4dda5067c49c9ef51adf966c51cf268e4991e741b45c803fc6c1e821051832c090ba2c685f03c4eaa7d
data/CHANGELOG.md CHANGED
@@ -1,6 +1,101 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [0.8.4] - 2021-10-xx
5
+
6
+ ### Added
7
+ - `Ecoportal::API::Internal::Permissions` added abilities
8
+ - `visitor_management`, `cross_register_reporting` and `broadcast_notifications`
9
+ - Some yardocs too
10
+ - Some callbacks are done in a non-obvious way and the returned object type was not documented
11
+ - For this reason, some yardocs have been added to some of the parts that have been worked on.
12
+
13
+ ### Fixed
14
+ - `Ecoportal::API::V1::People#create_job`
15
+ - **Removed** call to `BatchOperation#process_response`
16
+ - The method was is already called by `job_result`
17
+ - As a consequence there was a double up of `callbacks`
18
+ - **Fixed** line in wrong position
19
+ - `Ecoportal::API::V1::People#batch`
20
+ - `Ecoportal::API::Common::ElasticApmIntegration#unexpected_server_error?`
21
+ - No code or code lesser than 100 is a server error as well
22
+
23
+ ### Changed
24
+ - `Ecoportal::API::Common::Client`: changed
25
+ - Logging the **response** of batches or batch jobs can be handy when debugging the back-end
26
+ - **removed** method `#without_response_logging`
27
+ - This change entailed to remove dependencies in `Ecoportal::API::V1::People`
28
+ - Specifically in methods `#batch`, `#job_result` and `#create_job`
29
+ - `@response_logging_enabled` to be set in initialization stage (added parameter for `.new`)
30
+
31
+ ## [0.8.3] - 2021-05-24
32
+
33
+ ### Added
34
+ - `Ecoportal::API::Errors` namespace
35
+ - `Ecoportal::API::Errors::Base` base error class.
36
+ - `Ecoportal::API::Errors::TimeOut` error when an api request fails with time out.
37
+ - This serves the purpose to allow a client script to re-start the process where it stopped by capturing this specific Error
38
+ - `Ecoportal::API::Common::BaseModel::UnlinkedModel` added more description to track down the source of the error.
39
+ - `Ecoportal::API::Common::BaseModel#reset!` added parameter `key`, which should try to recover `doc[key]` from `original_doc[key]`
40
+ - Thanks to this, you are supposed to be able to do things like:
41
+ - `person.account = nil && person.reset!("account")`
42
+ - `person.name = nil && person.reset!("name")`
43
+ - `Ecoportal::API::V1::People#job` methods to provide more information on failure.
44
+ - Specific changes due to eP **release `1.5.9.70`** (_Policy Group Abilities_)
45
+ - `Ecoportal::API::Internal::Account#permissions_merged`
46
+ - `Ecoportal::API::Internal::Permissions#person_abilities` (new ability)
47
+ - `Ecoportal::API::Internal::Account#user_id`
48
+
49
+ ### Fixed
50
+ - `Ecoportal::API::Internal::Account`: consistency in setting arrays (`uniq!` & `compact`)
51
+ - `#policy_group_ids=`, `#login_provider_ids=`, `#starred_ids=`
52
+ - `Ecoportal::API::Common::HashDiff.diff` was including empty `{}` objects
53
+ - This change sacrifices the case `account: {}` (which will be also removed from `as_update`), but it should be fine.
54
+
55
+ ### Changed
56
+ - `Ecoportal::API::V1::People#job` to raise specific error on time out `API::Errors::TimeOut`
57
+ - Specific changes due to eP **release `1.5.9.70`** (_Policy Group Abilities_)
58
+ - `Ecoportal::API::Internal::Account` **removed** methods: `#permissions_preset`, `#preset` and `#preset=`
59
+ - `Ecoportal::API::Internal::Person#account=` added support for `user_id` which should remain unchanged when existing
60
+ - **remove** from `as_update` **read-only** data
61
+ - `Ecoportal::API::Common::HashDiff.diff` added parameter `:ignore` (`Array`)
62
+ - `Ecoportal::API::Common::BaseModel#as_update` added parameter `:ignore`
63
+ - `Ecoportal::API::V1::Person#as_update` added method, which ignores `subordinates`
64
+ - `Ecoportal::API::Internal::Person#as_update` added method, which ignores `user_id`, `permissions_merged` and `prefilter`
65
+ - `Ecoportal::API::Internal::Account#as_update` added method, which ignores `user_id`, `permissions_merged` and `prefilter`
66
+ - `Ecoportal::API::Common::Client` native support for `elastic-apm`
67
+ - Via new module `Ecoportal::API::Common::ElasticApmIntegration` with method `log_unexpected_server_error`, which will only log an `UnexpectedServerError` to _ElasticAPM_ if
68
+ 1. There's a correct configuration: environmental variables `ELASTIC_APM_KEY` and `ELASTIC_APM_ACCOUNT_ID` are defined
69
+ 2. The `Response` from the server gave `code` in the range `5xx` (which are those under server responsibility)
70
+ - `Ecoportal::API::Common::Client` added retry logics when `response.status == 5xx`
71
+
72
+ ## [0.8.2] - 2021-02-24
73
+
74
+ ### Added
75
+
76
+ ### Fixed
77
+ - `Ecoportal::API::V1::Person#filter_tags=` should ignore `nil` values
78
+
79
+ ### Changed
80
+ - removed all the namespace under `Ecoportal::API::V2` as that is managed by `ecoportal-api-oozes` gem
81
+ - url: https://rubygems.org/gems/ecoportal-api-oozes
82
+ - `Ecoportal::API::V1::People.get` should return a `Person` object
83
+ - observe that it was returning the `WrappedResponse` (an `Enumerable` helper that works better when getting multiple people).
84
+
85
+ ## [0.7.5] - 2021-02-12
86
+
87
+ ### Added
88
+
89
+ ### Fixed
90
+ - `pretty_print` method was colliding with `pp` module:
91
+ - renamed to `Ecoportal::API::Common::BaseModel#print_pretty`
92
+ - renamed to `Ecoportal::API::Common::BatchReponse#print_pretty`
93
+ - renamed to `Ecoportal::API::Common::Reponse#print_pretty`
94
+ - renamed to `Ecoportal::API::Common::WrappedResponse#print_pretty`
95
+
96
+ ### Changed
97
+ - forgot to change `Ecoportal::API::VERSION` during last release
98
+
4
99
  ## [0.7.4] - 2021-01-2
5
100
 
6
101
  ### Added
@@ -62,6 +157,10 @@ All notable changes to this project will be documented in this file.
62
157
  ## [0.7.0] - 2020-09-11
63
158
 
64
159
  ### Added
160
+ - added hook, **private** method `body_data` for child classes to define behaviour on `response.body` to
161
+ - `Ecoportal::API::V1::People`
162
+ - `Ecoportal::API::Common::BatchOperation`
163
+
65
164
  ### Changed
66
165
  - `Ecoportal::API::Internal::Permissions`: **update for new abilities of ecoPortal release `1.5.2`**
67
166
  - decoupled abilities: `person_core` into `person_core_create`, `person_core_edit`
@@ -13,6 +13,8 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = "https://www.ecoportal.com"
14
14
  spec.licenses = %w[MIT]
15
15
 
16
+ spec.required_ruby_version = '>= 2.4.4'
17
+
16
18
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
19
  f.match(%r{^(test|spec|features)/})
18
20
  end
@@ -20,13 +22,15 @@ Gem::Specification.new do |spec|
20
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
23
  spec.require_paths = ["lib"]
22
24
 
23
- spec.add_development_dependency "bundler", "~> 2.1", ">= 2.1.3"
24
- spec.add_development_dependency "rake", "~> 12.0"
25
- spec.add_development_dependency "rspec", "~> 3", ">= 3.9"
26
- spec.add_development_dependency "yard", "~> 0.9", ">= 0.9.18"
27
- spec.add_development_dependency "redcarpet", "~> 3.5", ">= 3.5.0"
28
- spec.add_development_dependency "pry" , "~> 0.13"
25
+ spec.add_development_dependency "bundler", ">= 2.2.17", "< 2.3"
26
+ spec.add_development_dependency "rspec", ">= 3.10.0", "< 3.11"
27
+ spec.add_development_dependency "rake", ">= 13.0.3", "< 13.1"
28
+ spec.add_development_dependency "yard", ">= 0.9.26", "< 0.10"
29
+ spec.add_development_dependency "redcarpet", ">= 3.5.1", "< 3.6"
30
+ spec.add_development_dependency "pry" , "~> 0.14"
29
31
 
30
- spec.add_dependency 'http', '~> 4'
32
+ spec.add_dependency 'http', '~> 4.4.1', "< 5"
33
+ spec.add_dependency 'dotenv', '>= 2.7.6', "< 2.8"
34
+ spec.add_dependency 'elastic-apm', '>= 4.0.0', "< 4.1"
31
35
  spec.add_dependency 'hash-polyfill', '~> 0'
32
36
  end
@@ -3,7 +3,9 @@ module Ecoportal
3
3
  module Common
4
4
  class BaseModel
5
5
  class UnlinkedModel < Exception
6
- def initialize (msg = "Something went wrong when linking the document.")
6
+ def initialize (msg = "Something went wrong when linking the document.", from: nil, key: nil)
7
+ msg += " From: #{from}." if from
8
+ msg += " key: #{key}." if key
7
9
  super(msg)
8
10
  end
9
11
  end
@@ -28,7 +30,12 @@ module Ecoportal
28
30
  var = "@#{method}".freeze
29
31
  key = key.to_s.freeze
30
32
  define_method(method) do
31
- return instance_variable_get(var) if instance_variable_defined?(var)
33
+ if instance_variable_defined?(var)
34
+ value = instance_variable_get(var)
35
+ return value unless nullable
36
+ return value if (value && doc[key]) || (!value && !doc[key])
37
+ remove_instance_variable(var)
38
+ end
32
39
  doc[key] ||= {} unless nullable
33
40
  return instance_variable_set(var, nil) unless doc[key]
34
41
 
@@ -53,19 +60,19 @@ module Ecoportal
53
60
  end
54
61
 
55
62
  def doc
56
- raise UnlinkedModel.new unless linked?
63
+ raise UnlinkedModel.new(from: "#{self.class}#doc", key: _key) unless linked?
57
64
  return @doc if is_root?
58
65
  _parent.doc.dig(*[_key].flatten)
59
66
  end
60
67
 
61
68
  def original_doc
62
- raise UnlinkedModel.new unless linked?
69
+ raise UnlinkedModel.new(from: "#{self.class}#original_doc", key: _key) unless linked?
63
70
  return @original_doc if is_root?
64
71
  _parent.original_doc.dig(*[_key].flatten)
65
72
  end
66
73
 
67
74
  def initial_doc
68
- raise UnlinkedModel.new unless linked?
75
+ raise UnlinkedModel.new(from: "#{self.class}#initial_doc", key: _key) unless linked?
69
76
  return @initial_doc if is_root?
70
77
  _parent.initial_doc.dig(*[_key].flatten)
71
78
  end
@@ -78,18 +85,19 @@ module Ecoportal
78
85
  doc.to_json(*args)
79
86
  end
80
87
 
81
- def as_update(ref = :last)
88
+ def as_update(ref = :last, ignore: [])
82
89
  new_doc = as_json
83
90
  ref_doc = ref == :total ? initial_doc : original_doc
84
- Common::HashDiff.diff(new_doc, ref_doc)
91
+ Common::HashDiff.diff(new_doc, ref_doc, ignore: ignore)
85
92
  end
86
93
 
87
94
  def dirty?
88
95
  as_update != {}
89
96
  end
90
97
 
98
+ # It consolidates all the changes carried by `doc` by setting it as `original_doc`.
91
99
  def consolidate!
92
- raise UnlinkedModel.new unless linked?
100
+ raise UnlinkedModel.new(from: "#{self.class}#consolidate!", key: _key) unless linked?
93
101
  new_doc = JSON.parse(doc.to_json)
94
102
  if is_root?
95
103
  @original_doc = new_doc
@@ -98,20 +106,39 @@ module Ecoportal
98
106
  end
99
107
  end
100
108
 
101
- def reset!
102
- raise UnlinkedModel.new unless linked?
103
- new_doc = JSON.parse(original_doc.to_json)
104
- if is_root?
105
- @doc = new_doc
109
+ # It removes all the changes carried by `doc` by restoring `original_doc` into `doc`.
110
+ # @note
111
+ # 1. When there are nullable properties, it may be required to apply `reset!` from the parent
112
+ # i.e. `parent.reset!("child")` # when parent.child is `nil`
113
+ # 2. In such a case, only immediate childs are allowed to be reset
114
+ # @param key [String, Array<String>, nil] if given, it only resets the specified property
115
+ def reset!(key = nil)
116
+ raise "'key' should be a String. Given #{key}" unless !key || key.is_a?(String)
117
+ raise UnlinkedModel.new(from: "#{self.class}#reset!", key: _key) unless linked?
118
+
119
+ if key
120
+ if self.respond_to?(key) && child = self.send(key) && child.is_a?(Ecoportal::API::Common::BaseModel)
121
+ child.reset!
122
+ else
123
+ new_doc = original_doc && original_doc[key]
124
+ dig_set(doc, [key], new_doc && JSON.parse(new_doc.to_json))
125
+ # regenerate object if new_doc is null
126
+ self.send(key) if !new_doc && self.respond_to?(key)
127
+ end
106
128
  else
107
- dig_set(_parent.doc, [_key].flatten, new_doc)
129
+ new_doc = JSON.parse(original_doc.to_json)
130
+ if is_root?
131
+ @doc = new_doc
132
+ else
133
+ dig_set(_parent.doc, [_key].flatten, new_doc)
134
+ end
108
135
  end
109
136
  end
110
137
 
111
- # def pretty_print
112
- # puts JSON.pretty_generate(as_json)
113
- # self
114
- # end
138
+ def print_pretty
139
+ puts JSON.pretty_generate(as_json)
140
+ self
141
+ end
115
142
 
116
143
  protected
117
144
 
@@ -133,6 +160,17 @@ module Ecoportal
133
160
  end
134
161
  end
135
162
 
163
+ def set_uniq_array_keep_order(key, value)
164
+ unless value.is_a?(Array)
165
+ raise "#{key}= needs to be passed an Array, got #{value.class}"
166
+ end
167
+ ini_vals = (original_doc && original_doc[key]) || []
168
+
169
+ value = value.uniq
170
+ # preserve original order to avoid false updates
171
+ doc[key] = ((ini_vals & value) + (value - ini_vals)).compact
172
+ end
173
+
136
174
  end
137
175
  end
138
176
  end
@@ -27,7 +27,7 @@ module Ecoportal
27
27
 
28
28
  log(:info) { "Processing batch responses" }
29
29
 
30
- response.body.each.with_index do |subresponse, idx|
30
+ body_data(response.body).each.with_index do |subresponse, idx|
31
31
  callback = @operations[idx][:callback]
32
32
  status = subresponse["status"]
33
33
  body = subresponse["response"]
@@ -97,6 +97,12 @@ module Ecoportal
97
97
 
98
98
  private
99
99
 
100
+ # Hook for other api versions to obtain the raw data of a response
101
+ # @note this was introduced to allow `v2` to reuse this class
102
+ def body_data(body)
103
+ body
104
+ end
105
+
100
106
  def log_batch_response(operation, response)
101
107
  level = response.success?? :debug : :warn
102
108
  log(:info) { "BATCH #{operation[:method]} #{operation[:path]}" }
@@ -21,9 +21,9 @@ module Ecoportal
21
21
  end
22
22
  end
23
23
 
24
- def pretty_print
24
+ def print_pretty
25
25
  if success?
26
- each(&:pretty_print)
26
+ each(&:print_pretty)
27
27
  else
28
28
  puts "Request failed."
29
29
  end
@@ -10,6 +10,9 @@ module Ecoportal
10
10
  # - to return `HTTP::Response` ([response.rb](https://github.com/httprb/http/blob/master/lib/http/response.rb))
11
11
  # @attr_reader logger [Logger] the logger.
12
12
  class Client
13
+ include Common::ElasticApmIntegration
14
+ DELAY_REQUEST_RETRY = 5
15
+
13
16
  attr_accessor :logger
14
17
 
15
18
  # @note the `api_key` will be automatically added as parameter `X-ApiKey` in the header of the http requests.
@@ -17,11 +20,14 @@ module Ecoportal
17
20
  # @param version [String] it is part of the base url and will determine the api version we query against.
18
21
  # @param host [String] api server domain.
19
22
  # @param logger [Logger] an object with `Logger` interface to generate logs.
23
+ # @param response_logging [Boolean] whether or not batch responses should be logged
20
24
  # @return [Client] an object that holds the configuration of the api connection.
21
- def initialize(api_key:, version: "v1", host: "live.ecoportal.com", logger: nil)
25
+ def initialize(api_key:, version: "v1", host: "live.ecoportal.com", logger: nil, response_logging: false)
22
26
  @version = version
23
27
  @api_key = api_key
24
28
  @logger = logger
29
+ @host = host
30
+ @response_logging_enabled = response_logging
25
31
  if host.match(/^localhost|^127\.0\.0\.1/)
26
32
  @base_uri = "http://#{host}/api/"
27
33
  else
@@ -31,7 +37,6 @@ module Ecoportal
31
37
  if @api_key.nil? || @api_key.match(/\A\W*\z/)
32
38
  log(:error) { "Api-key missing!" }
33
39
  end
34
- @response_logging_enabled = true
35
40
  end
36
41
 
37
42
  # Logger interface.
@@ -106,7 +111,7 @@ module Ecoportal
106
111
  # basic HTTP connection to the block.
107
112
  # @yield [http] launch specific http request.
108
113
  # @yieldparam http [HTTP] the http connection.
109
- # @yieldreturn [Common::Response] the basic custom reponse object.
114
+ # @yieldreturn [Common::Response] the basic custom response object.
110
115
  # @return [Common::Reponse] the basic custom response object.
111
116
  def request
112
117
  wrap_response yield(base_request)
@@ -140,22 +145,15 @@ module Ecoportal
140
145
  @base_uri+@version+path
141
146
  end
142
147
 
143
- def without_response_logging(&block)
144
- begin
145
- @response_logging_enabled = false
146
- yield self
147
- ensure
148
- @response_logging_enabled = true
149
- end
150
- end
151
-
152
148
  private
153
149
 
154
- def instrument(method, path, data = nil)
150
+ def instrument(method, path, data = nil, &block)
151
+ raise "Expected block" unless block
155
152
  start_time = Time.now.to_f
156
- log(:info) { "#{method} #{url_for(path)}" }
153
+ log(:info) { "#{method} #{url_for(path)}" }
157
154
  log(:debug) { "Data: #{JSON.pretty_generate(data)}" }
158
- yield.tap do |result|
155
+
156
+ with_retry(&block).tap do |result|
159
157
  end_time = Time.now.to_f
160
158
  log(result.success?? :info : :warn) do
161
159
  "Took %.2fs, Status #{result.status}" % (end_time - start_time)
@@ -165,6 +163,33 @@ module Ecoportal
165
163
  end if @response_logging_enabled
166
164
  end
167
165
  end
166
+
167
+ # Helper to ensure unexpected server errors do not bring client scripts immediately down
168
+ def with_retry(attempts = 3, delay = DELAY_REQUEST_RETRY, error_safe: true, &block)
169
+ response = nil
170
+ attempts.times do |i|
171
+ remaining = attempts - i - 1
172
+ begin
173
+ response = block.call
174
+ rescue HTTP::ConnectionError => e
175
+ raise unless error_safe && remaining > 0
176
+ log(:error) { "Got connection error: #{e.message}" }
177
+ response = with_retry(remaining, error_safe: error_safe, &block)
178
+ rescue IOError => e
179
+ raise unless error_safe && remaining > 0
180
+ log(:error) { "Got IO error: #{e.message}" }
181
+ response = with_retry(remaining, error_safe: error_safe, &block)
182
+ end
183
+ return response unless unexpected_server_error?(response.status)
184
+ log_unexpected_server_error(response)
185
+ msg = "Got server error (#{response.status}): #{response.body}\n"
186
+ msg += "Going to retry (#{i} out of #{attempts})"
187
+ log(:error) { msg }
188
+ sleep(delay) if i < attempts
189
+ end
190
+ response
191
+ end
192
+
168
193
  end
169
194
  end
170
195
  end
@@ -0,0 +1,112 @@
1
+ require 'elastic-apm'
2
+ module Ecoportal
3
+ module API
4
+ module Common
5
+ module ElasticApmIntegration
6
+
7
+ class UnexpectedServerError < StandardError
8
+ def initialize(code, msg)
9
+ super("Code: #{code} -- Error: #{msg}")
10
+ end
11
+ end
12
+
13
+ APM_SERVICE_NAME = 'ecoportal-api-gem'
14
+
15
+ # Log only errors that are only server's responsibility
16
+ def log_unexpected_server_error(response)
17
+ raise "Expecting Ecoportal::API::Common::Response. Given: #{response.class}" unless response.is_a?(Common::Response)
18
+ return nil unless elastic_apm_service
19
+ return nil unless unexpected_server_error?(response.status)
20
+ if ElasticAPM.running?
21
+ ElasticAPM.report(UnexpectedServerError.new(response.status, response.body))
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def unexpected_server_error?(code)
28
+ !code || ((code >= 500) && (code <= 599)) || (code <= 99)
29
+ end
30
+
31
+ # finalizer to stop the agent
32
+ close_elastic_apm = Proc.new do |id|
33
+ begin
34
+ if ElasticAPM.running?
35
+ puts "Stopping ElasticAPM service"
36
+ ElasticAPM.stop
37
+ end
38
+ rescue StandardError => e
39
+ # Silent
40
+ end
41
+ end
42
+ ObjectSpace.define_finalizer("ElasticAPM", close_elastic_apm)
43
+
44
+ def elastic_apm_service
45
+ return false if @disable_apm
46
+ begin
47
+ ElasticAPM.start(**elastic_apm_options) unless ElasticAPM.running?
48
+ rescue StandardError => e
49
+ @disable_apm = true
50
+ puts "ElasticAPM services not available: #{e}"
51
+ end
52
+ end
53
+
54
+ def elastic_apm_options
55
+ {
56
+ service_name: APM_SERVICE_NAME,
57
+ server_url: elastic_apm_url,
58
+ secret_token: elastic_apm_key,
59
+ environment: environment,
60
+ #http_compression: false,
61
+ transaction_sample_rate: 0.1,
62
+ transaction_max_spans: 100,
63
+ span_frames_min_duration: "5ms"
64
+ }.tap do |options|
65
+ options.merge!({
66
+ log_level: Logger::DEBUG,
67
+ log_path: File.join(__dir__, "elastic_apm.log")
68
+ }) if false
69
+ end
70
+ end
71
+
72
+ def elastic_apm_url
73
+ @elastic_apm_url ||= "https://".tap do |url|
74
+ url << "#{elastic_apm_account_id}"
75
+ url << ".#{elastic_apm_base_url}"
76
+ url << ":#{elastic_apm_port}"
77
+ end
78
+ end
79
+
80
+ def elastic_apm_key
81
+ @elastic_apm_key ||= ENV['ELASTIC_APM_KEY']
82
+ end
83
+
84
+ def elastic_apm_account_id
85
+ @elastic_apm_account_id ||= ENV['ELASTIC_APM_ACCOUNT_ID']
86
+ end
87
+
88
+ def elastic_apm_base_url
89
+ @elastic_apm_base_url ||= "apm.#{elastic_apm_region}.aws.cloud.es.io"
90
+ end
91
+
92
+ def elastic_apm_region
93
+ @elastic_apm_region ||= ENV['ELASTIC_APM_REGION'] || "ap-southeast-2"
94
+ end
95
+
96
+
97
+ def elastic_apm_port
98
+ @elastic_apm_port ||= ENV['ELASTIC_APM_PORT'] || "443"
99
+ end
100
+
101
+ def environment
102
+ @environment ||= "unknown".tap do |value|
103
+ if instance_variable_defined?(:@host) && env = @host.gsub(".ecoportal.com", '')
104
+ value.clear << env
105
+ end
106
+ end
107
+ end
108
+
109
+ end
110
+ end
111
+ end
112
+ end
@@ -5,15 +5,16 @@ module Ecoportal
5
5
  ID_KEYS = %w[id]
6
6
 
7
7
  class << self
8
- def diff(a, b)
9
- return a if a.class != b.class
8
+
9
+ def diff(a, b, ignore: [])
10
10
  case a
11
11
  when Hash
12
12
  {}.tap do |diffed|
13
13
  a.each do |key, a_value|
14
- b_value = b[key]
15
- next if a_value == b_value && !ID_KEYS.include?(key)
16
- diffed[key] = diff(a_value, b_value)
14
+ b_value = b && b[key]
15
+ no_changes = (a_value == b_value) || ignore.include?(key)
16
+ next if !ID_KEYS.include?(key) && no_changes
17
+ diffed[key] = diff(a_value, b_value, ignore: ignore)
17
18
  diffed.delete(key) if diffed[key] == {}
18
19
  end
19
20
  # All keys are IDs, so it's actually blank
@@ -22,10 +23,10 @@ module Ecoportal
22
23
  end
23
24
  end
24
25
  when Array
25
- return a unless a.length == b.length
26
+ return a unless b.is_a?(Array) && a.length == b.length
26
27
  a.map.with_index do |a_value, idx|
27
28
  b_value = b[idx]
28
- diff(a_value, b_value)
29
+ diff(a_value, b_value, ignore: ignore)
29
30
  end.reject do |el|
30
31
  el == {}
31
32
  end
@@ -20,7 +20,7 @@ module Ecoportal
20
20
  @status.success?
21
21
  end
22
22
 
23
- def pretty_print
23
+ def print_pretty
24
24
  puts "Status: #{@status.code}"
25
25
  puts "Body:"
26
26
  puts JSON.pretty_generate(@body)
@@ -40,9 +40,9 @@ module Ecoportal
40
40
  response.success?
41
41
  end
42
42
 
43
- def pretty_print
43
+ def print_pretty
44
44
  if success?
45
- each(&:pretty_print)
45
+ each(&:print_pretty)
46
46
  else
47
47
  puts "Request failed."
48
48
  end
@@ -10,6 +10,7 @@ require 'ecoportal/api/common/hash_diff'
10
10
  require 'ecoportal/api/common/base_model'
11
11
  require 'ecoportal/api/common/doc_helpers'
12
12
  require 'ecoportal/api/common/logging'
13
+ require 'ecoportal/api/common/elastic_apm_integration'
13
14
  require 'ecoportal/api/common/client'
14
15
  require 'ecoportal/api/common/response'
15
16
  require 'ecoportal/api/common/wrapped_response'
@@ -0,0 +1,8 @@
1
+ module Ecoportal
2
+ module API
3
+ module Errors
4
+ class Base < StandardError
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Ecoportal
2
+ module API
3
+ module Errors
4
+ class TimeOut < Errors::Base
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module Ecoportal
2
+ module API
3
+ module Errors
4
+ end
5
+ end
6
+ end
7
+
8
+ require 'ecoportal/api/errors/base'
9
+ require 'ecoportal/api/errors/time_out'