ecoportal-api 0.8.2 → 0.8.5
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 +86 -1
- data/ecoportal-api.gemspec +3 -1
- data/lib/ecoportal/api/common/base_model.rb +54 -16
- data/lib/ecoportal/api/common/client.rb +40 -15
- data/lib/ecoportal/api/common/elastic_apm_integration.rb +112 -0
- data/lib/ecoportal/api/common/hash_diff.rb +8 -7
- data/lib/ecoportal/api/common.rb +1 -0
- data/lib/ecoportal/api/errors/base.rb +8 -0
- data/lib/ecoportal/api/errors/time_out.rb +8 -0
- data/lib/ecoportal/api/errors.rb +9 -0
- data/lib/ecoportal/api/internal/account.rb +34 -39
- data/lib/ecoportal/api/internal/permissions.rb +4 -3
- data/lib/ecoportal/api/internal/person.rb +8 -2
- data/lib/ecoportal/api/v1/people.rb +27 -21
- data/lib/ecoportal/api/v1/person.rb +6 -6
- data/lib/ecoportal/api/v1/person_details.rb +12 -1
- data/lib/ecoportal/api/v1/schema_field_value.rb +16 -19
- data/lib/ecoportal/api/version.rb +1 -1
- data/lib/ecoportal/api.rb +2 -0
- metadata +49 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4364abf59cd6460788d315eb5012df5e8ad254a208e8af7314a766463eb35ed0
|
|
4
|
+
data.tar.gz: d71fe7e38402e827aea0f95c6a0c379e191e525d597ab8b225990f48c6879764
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e055587b4aafbfabf25996cf4e67d85b0a0d9be936d4b0bdae194b431e4f2bb12b09fc99562d594cf355767aae3081a1fbea70d10a48dc204ce839284bbf0256
|
|
7
|
+
data.tar.gz: 5cd7284e455af271f1642775844636fd0ea11b590c0036d3b4a43868a9ffd6ece97ceebb9a3d6dfcdae7891dd65dedd0df9dac5944578cb18484b07342640c3e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,92 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
All notable changes to this project will be documented in this file.
|
|
3
3
|
|
|
4
|
-
## [0.8.
|
|
4
|
+
## [0.8.5] - 2022-02-28
|
|
5
|
+
|
|
6
|
+
### Added
|
|
7
|
+
- `Ecoportal::API::V1::PersonDetails#[]` to raise a specific error type to allow handling
|
|
8
|
+
- `Ecoportal::API::V1::PersonDetails.key?` to allow to check if a field exists
|
|
9
|
+
- `Ecoportal::API::Internal::Account#force_send_invites` support for back-end new method
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- `Ecoportal::API::V1::People#get` fixed typo
|
|
13
|
+
- `Ecoportal::API::Common::BaseModel` `#original_doc` and `#initial_doc` maybe empty for the parent object
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- `Ecoportal::API::V1::People#each` limited the `GET` retries to `5`
|
|
17
|
+
- `Ecoportal::API::Internal::Account#default_tag=`
|
|
18
|
+
- Controls input type to be `String` or `nil`
|
|
19
|
+
- Inherent `upcase`
|
|
20
|
+
|
|
21
|
+
## [0.8.4] - 2021-11-05
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- `Ecoportal::API::Internal::Permissions` added abilities
|
|
25
|
+
- `visitor_management`, `cross_register_reporting` and `broadcast_notifications`
|
|
26
|
+
- Some yardocs too
|
|
27
|
+
- Some callbacks are done in a non-obvious way and the returned object type was not documented
|
|
28
|
+
- For this reason, some yardocs have been added to some of the parts that have been worked on.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- `Ecoportal::API::V1::People#create_job`
|
|
32
|
+
- **Removed** call to `BatchOperation#process_response`
|
|
33
|
+
- The method was is already called by `job_result`
|
|
34
|
+
- As a consequence there was a double up of `callbacks`
|
|
35
|
+
- **Fixed** line in wrong position
|
|
36
|
+
- `Ecoportal::API::V1::People#batch`
|
|
37
|
+
- `Ecoportal::API::Common::ElasticApmIntegration#unexpected_server_error?`
|
|
38
|
+
- No code or code lesser than 100 is a server error as well
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- `Ecoportal::API::Common::Client`: changed
|
|
42
|
+
- Logging the **response** of batches or batch jobs can be handy when debugging the back-end
|
|
43
|
+
- **removed** method `#without_response_logging`
|
|
44
|
+
- This change entailed to remove dependencies in `Ecoportal::API::V1::People`
|
|
45
|
+
- Specifically in methods `#batch`, `#job_result` and `#create_job`
|
|
46
|
+
- `@response_logging_enabled` to be set in initialization stage (added parameter for `.new`)
|
|
47
|
+
|
|
48
|
+
## [0.8.3] - 2021-05-24
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
- `Ecoportal::API::Errors` namespace
|
|
52
|
+
- `Ecoportal::API::Errors::Base` base error class.
|
|
53
|
+
- `Ecoportal::API::Errors::TimeOut` error when an api request fails with time out.
|
|
54
|
+
- This serves the purpose to allow a client script to re-start the process where it stopped by capturing this specific Error
|
|
55
|
+
- `Ecoportal::API::Common::BaseModel::UnlinkedModel` added more description to track down the source of the error.
|
|
56
|
+
- `Ecoportal::API::Common::BaseModel#reset!` added parameter `key`, which should try to recover `doc[key]` from `original_doc[key]`
|
|
57
|
+
- Thanks to this, you are supposed to be able to do things like:
|
|
58
|
+
- `person.account = nil && person.reset!("account")`
|
|
59
|
+
- `person.name = nil && person.reset!("name")`
|
|
60
|
+
- `Ecoportal::API::V1::People#job` methods to provide more information on failure.
|
|
61
|
+
- Specific changes due to eP **release `1.5.9.70`** (_Policy Group Abilities_)
|
|
62
|
+
- `Ecoportal::API::Internal::Account#permissions_merged`
|
|
63
|
+
- `Ecoportal::API::Internal::Permissions#person_abilities` (new ability)
|
|
64
|
+
- `Ecoportal::API::Internal::Account#user_id`
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
- `Ecoportal::API::Internal::Account`: consistency in setting arrays (`uniq!` & `compact`)
|
|
68
|
+
- `#policy_group_ids=`, `#login_provider_ids=`, `#starred_ids=`
|
|
69
|
+
- `Ecoportal::API::Common::HashDiff.diff` was including empty `{}` objects
|
|
70
|
+
- This change sacrifices the case `account: {}` (which will be also removed from `as_update`), but it should be fine.
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
- `Ecoportal::API::V1::People#job` to raise specific error on time out `API::Errors::TimeOut`
|
|
74
|
+
- Specific changes due to eP **release `1.5.9.70`** (_Policy Group Abilities_)
|
|
75
|
+
- `Ecoportal::API::Internal::Account` **removed** methods: `#permissions_preset`, `#preset` and `#preset=`
|
|
76
|
+
- `Ecoportal::API::Internal::Person#account=` added support for `user_id` which should remain unchanged when existing
|
|
77
|
+
- **remove** from `as_update` **read-only** data
|
|
78
|
+
- `Ecoportal::API::Common::HashDiff.diff` added parameter `:ignore` (`Array`)
|
|
79
|
+
- `Ecoportal::API::Common::BaseModel#as_update` added parameter `:ignore`
|
|
80
|
+
- `Ecoportal::API::V1::Person#as_update` added method, which ignores `subordinates`
|
|
81
|
+
- `Ecoportal::API::Internal::Person#as_update` added method, which ignores `user_id`, `permissions_merged` and `prefilter`
|
|
82
|
+
- `Ecoportal::API::Internal::Account#as_update` added method, which ignores `user_id`, `permissions_merged` and `prefilter`
|
|
83
|
+
- `Ecoportal::API::Common::Client` native support for `elastic-apm`
|
|
84
|
+
- Via new module `Ecoportal::API::Common::ElasticApmIntegration` with method `log_unexpected_server_error`, which will only log an `UnexpectedServerError` to _ElasticAPM_ if
|
|
85
|
+
1. There's a correct configuration: environmental variables `ELASTIC_APM_KEY` and `ELASTIC_APM_ACCOUNT_ID` are defined
|
|
86
|
+
2. The `Response` from the server gave `code` in the range `5xx` (which are those under server responsibility)
|
|
87
|
+
- `Ecoportal::API::Common::Client` added retry logics when `response.status == 5xx`
|
|
88
|
+
|
|
89
|
+
## [0.8.2] - 2021-02-24
|
|
5
90
|
|
|
6
91
|
### Added
|
|
7
92
|
|
data/ecoportal-api.gemspec
CHANGED
|
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
23
23
|
spec.require_paths = ["lib"]
|
|
24
24
|
|
|
25
|
-
spec.add_development_dependency "bundler", ">= 2.2.
|
|
25
|
+
spec.add_development_dependency "bundler", ">= 2.2.17", "< 2.3"
|
|
26
26
|
spec.add_development_dependency "rspec", ">= 3.10.0", "< 3.11"
|
|
27
27
|
spec.add_development_dependency "rake", ">= 13.0.3", "< 13.1"
|
|
28
28
|
spec.add_development_dependency "yard", ">= 0.9.26", "< 0.10"
|
|
@@ -30,5 +30,7 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
spec.add_development_dependency "pry" , "~> 0.14"
|
|
31
31
|
|
|
32
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"
|
|
33
35
|
spec.add_dependency 'hash-polyfill', '~> 0'
|
|
34
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
|
-
|
|
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,21 +60,21 @@ 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
|
-
_parent.original_doc
|
|
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
|
-
_parent.initial_doc
|
|
77
|
+
_parent.initial_doc&.dig(*[_key].flatten)
|
|
71
78
|
end
|
|
72
79
|
|
|
73
80
|
def as_json
|
|
@@ -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,13 +106,32 @@ module Ecoportal
|
|
|
98
106
|
end
|
|
99
107
|
end
|
|
100
108
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -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
|
|
@@ -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
|
|
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)
|
|
153
|
+
log(:info) { "#{method} #{url_for(path)}" }
|
|
157
154
|
log(:debug) { "Data: #{JSON.pretty_generate(data)}" }
|
|
158
|
-
|
|
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
|
-
|
|
9
|
-
|
|
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
|
|
15
|
-
|
|
16
|
-
|
|
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
|
data/lib/ecoportal/api/common.rb
CHANGED
|
@@ -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'
|
|
@@ -2,29 +2,43 @@ module Ecoportal
|
|
|
2
2
|
module API
|
|
3
3
|
class Internal
|
|
4
4
|
class Account < Common::BaseModel
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
PROPERTIES = [
|
|
6
|
+
"user_id", "policy_group_ids", "default_tag", "prefilter",
|
|
7
|
+
"permissions_custom", "permissions_merged", "preferences",
|
|
8
|
+
"login_provider_ids", "starred_ids", "landing_page_id",
|
|
9
|
+
"accept_eula", "send_invites", "force_send_invites"
|
|
10
|
+
]
|
|
11
|
+
passthrough *PROPERTIES.map(&:to_sym)
|
|
8
12
|
|
|
9
13
|
class_resolver :preferences_class, "Ecoportal::API::Internal::Preferences"
|
|
10
14
|
class_resolver :permissions_class, "Ecoportal::API::Internal::Permissions"
|
|
11
15
|
|
|
12
16
|
embeds_one :permissions, key: "permissions_custom", klass: :permissions_class
|
|
17
|
+
embeds_one :perms_merged, key: "permissions_merged", klass: :permissions_class
|
|
13
18
|
embeds_one :preferences, klass: :preferences_class
|
|
14
19
|
|
|
20
|
+
# Sets the `default_tag` of the user
|
|
21
|
+
# @note it upcases the value
|
|
22
|
+
# @param value [String, nil] the tag
|
|
23
|
+
# @return [String, nil] the value set in `default_tag`
|
|
24
|
+
def default_tag=(value)
|
|
25
|
+
unless !value || value.is_a?(String)
|
|
26
|
+
raise ArgumentError.new("default_tag= needs to be passed a String or nil, got #{value.class}")
|
|
27
|
+
end
|
|
28
|
+
if value
|
|
29
|
+
unless value.match(Ecoportal::API::V1::Person::VALID_TAG_REGEX)
|
|
30
|
+
raise ArgumentError.new("Invalid default tag #{value.inspect}")
|
|
31
|
+
end
|
|
32
|
+
value = value.upcase
|
|
33
|
+
end
|
|
34
|
+
doc["default_tag"] = value
|
|
35
|
+
end
|
|
36
|
+
|
|
15
37
|
# Sets the `policy_group_ids`
|
|
16
38
|
# @note it preserves the original order
|
|
17
39
|
# @param value [Array<String>] the policy group ids to be set.
|
|
18
40
|
def policy_group_ids=(value)
|
|
19
|
-
|
|
20
|
-
raise "policy_group_ids= needs to be passed an Array, got #{value.class}"
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
value.uniq!
|
|
24
|
-
ini_ids = (original_doc && original_doc["policy_group_ids"]) || []
|
|
25
|
-
# preserve original order to avoid false updates
|
|
26
|
-
doc["policy_group_ids"] = (ini_ids & value) + (value - ini_ids)
|
|
27
|
-
doc["policy_group_ids"].compact
|
|
41
|
+
set_uniq_array_keep_order("policy_group_ids", value)
|
|
28
42
|
end
|
|
29
43
|
|
|
30
44
|
# @return [Array<String>] the policy group ids of this user.
|
|
@@ -34,10 +48,7 @@ module Ecoportal
|
|
|
34
48
|
|
|
35
49
|
# Sets the `login_provider_ids`
|
|
36
50
|
def login_provider_ids=(value)
|
|
37
|
-
|
|
38
|
-
raise "login_provider_ids= needs to be passed an Array, got #{value.class}"
|
|
39
|
-
end
|
|
40
|
-
doc["login_provider_ids"] = value.compact
|
|
51
|
+
set_uniq_array_keep_order("login_provider_ids", value)
|
|
41
52
|
end
|
|
42
53
|
|
|
43
54
|
# @return [Array<String>] the login provider ids of this user.
|
|
@@ -47,10 +58,7 @@ module Ecoportal
|
|
|
47
58
|
|
|
48
59
|
# Sets the `starred_ids`
|
|
49
60
|
def starred_ids=(value)
|
|
50
|
-
|
|
51
|
-
raise "starred_ids= needs to be passed an Array, got #{value.class}"
|
|
52
|
-
end
|
|
53
|
-
doc["starred_ids"] = value.compact
|
|
61
|
+
set_uniq_array_keep_order("starred_ids", value)
|
|
54
62
|
end
|
|
55
63
|
|
|
56
64
|
# @return [Array<String>] the starred page ids of this user.
|
|
@@ -58,20 +66,6 @@ module Ecoportal
|
|
|
58
66
|
doc["starred_ids"] ||= []
|
|
59
67
|
end
|
|
60
68
|
|
|
61
|
-
# Sets the `permissions_preset`.
|
|
62
|
-
# @note basically the same as `permissions_preset=` but when `"custom"`, it's changed to `nil`
|
|
63
|
-
# @param value [nil, String] preset name.
|
|
64
|
-
def preset=(value)
|
|
65
|
-
self.permissions_preset = value == "custom" ? nil : value
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Gets the `permissions_preset`.
|
|
69
|
-
# @note basically the same as `permissions_preset` but when 'nil', it returns `"custom"` instead
|
|
70
|
-
# @return [nil, String] preset name.
|
|
71
|
-
def preset
|
|
72
|
-
self.permissions_preset.nil? ? "custom" : self.permissions_preset
|
|
73
|
-
end
|
|
74
|
-
|
|
75
69
|
# It preserves the values of keys that are not defined in `value`.
|
|
76
70
|
# @param value [Hash] the abilities that you want to update.
|
|
77
71
|
def permissions_custom=(value)
|
|
@@ -88,15 +82,16 @@ module Ecoportal
|
|
|
88
82
|
|
|
89
83
|
def as_json
|
|
90
84
|
super.tap do |hash|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
else
|
|
94
|
-
hash.delete "permissions_custom"
|
|
95
|
-
end
|
|
85
|
+
hash["permissions_custom"] = permissions.as_json
|
|
86
|
+
hash["permissions_merged"] = perms_merged.as_json
|
|
96
87
|
hash["preferences"] = preferences.as_json
|
|
97
88
|
end
|
|
98
89
|
end
|
|
99
90
|
|
|
91
|
+
def as_update(ref = :last, ignore: [])
|
|
92
|
+
super(ref, ignore: ignore | ["user_id", "permissions_merged", "prefilter"])
|
|
93
|
+
end
|
|
94
|
+
|
|
100
95
|
end
|
|
101
96
|
end
|
|
102
97
|
end
|
|
@@ -3,9 +3,10 @@ module Ecoportal
|
|
|
3
3
|
class Internal
|
|
4
4
|
class Permissions < Common::BaseModel
|
|
5
5
|
passthrough :files, :data, :reports
|
|
6
|
-
passthrough :organization, :
|
|
7
|
-
passthrough :
|
|
8
|
-
passthrough :
|
|
6
|
+
passthrough :organization, :pages, :page_editor, :registers, :tasks
|
|
7
|
+
passthrough :person_core, :person_core_create, :person_core_edit
|
|
8
|
+
passthrough :person_details, :person_account, :person_abilities
|
|
9
|
+
passthrough :visitor_management, :broadcast_notifications, :cross_register_reporting
|
|
9
10
|
end
|
|
10
11
|
end
|
|
11
12
|
end
|
|
@@ -11,10 +11,14 @@ module Ecoportal
|
|
|
11
11
|
super.update("account" => account&.as_json)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
def as_update(ref = :last, ignore: [])
|
|
15
|
+
super(ref, ignore: ignore | ["user_id", "permissions_merged", "prefilter"])
|
|
16
|
+
end
|
|
17
|
+
|
|
14
18
|
# Sets the Account to the person, depending on the paramter received:
|
|
15
19
|
# - `nil`: blanks the account.
|
|
16
20
|
# - `Account`: sets a copy of the object param as account.
|
|
17
|
-
# - `Hash`: slices the properties of `Account
|
|
21
|
+
# - `Hash`: slices the properties of `Account` (keeping the value of `user_id` if there was already account).
|
|
18
22
|
# @note this method does not make dirty the account (meaning that `as_json` will be an empty hash `{}`)
|
|
19
23
|
# @param value [nil, Account, Hash] value to be set.
|
|
20
24
|
# @return [nil, Account] the resulting `Account` set to the person.
|
|
@@ -25,7 +29,9 @@ module Ecoportal
|
|
|
25
29
|
when Internal::Account
|
|
26
30
|
doc["account"] = JSON.parse(value.to_json)
|
|
27
31
|
when Hash
|
|
28
|
-
|
|
32
|
+
user_id = account.user_id if account
|
|
33
|
+
doc["account"] = value.slice(*Internal::Account::PROPERTIES)
|
|
34
|
+
doc["account"]["user_id"] = user_id if user_id
|
|
29
35
|
else
|
|
30
36
|
# TODO
|
|
31
37
|
raise "Invalid set on account: Need nil, Account or Hash; got #{value.class}"
|
|
@@ -36,8 +36,15 @@ module Ecoportal
|
|
|
36
36
|
puts "\n" unless silent
|
|
37
37
|
loop do
|
|
38
38
|
params.update(cursor_id: cursor_id) if cursor_id
|
|
39
|
-
response =
|
|
40
|
-
|
|
39
|
+
body = nil; response = nil; count = 5
|
|
40
|
+
loop do
|
|
41
|
+
response = client.get("/people", params: params)
|
|
42
|
+
body = response && body_data(response.body)
|
|
43
|
+
break if response.success? || count <= 0
|
|
44
|
+
puts "Request failed - Status #{response.status}: #{body}"
|
|
45
|
+
count -= 1
|
|
46
|
+
sleep(0.5)
|
|
47
|
+
end
|
|
41
48
|
raise "Request failed - Status #{response.status}: #{body}" unless response.success?
|
|
42
49
|
|
|
43
50
|
unless silent || (total = body["total_results"]) == 0
|
|
@@ -77,7 +84,7 @@ module Ecoportal
|
|
|
77
84
|
response = client.get("/people/"+CGI.escape(id))
|
|
78
85
|
body = body_data(response.body)
|
|
79
86
|
return person_class.new(body) if response.success?
|
|
80
|
-
raise "Could not get person #{id} - Error #{
|
|
87
|
+
raise "Could not get person #{id} - Error #{response.status}: #{body}"
|
|
81
88
|
end
|
|
82
89
|
|
|
83
90
|
# Requests an update of a person via api.
|
|
@@ -117,29 +124,30 @@ module Ecoportal
|
|
|
117
124
|
# Creates a `BatchOperation` and yields it to the given bock.
|
|
118
125
|
# @yield [batch_op] adds multiple api requests for the current batch.
|
|
119
126
|
# @yieldparam batch_op [BatchOperation]
|
|
127
|
+
# @param job_mode [Boolean] whether or not it should use batch jobs
|
|
128
|
+
# @return [Ecoportal::API::Common::Response] the results of the batch
|
|
120
129
|
def batch(job_mode: true, &block)
|
|
121
130
|
return job(&block) if job_mode
|
|
122
131
|
operation = Common::BatchOperation.new("/people", person_class, logger: client.logger)
|
|
123
132
|
yield operation
|
|
124
133
|
# The batch operation is responsible for logging the output
|
|
125
|
-
client.
|
|
126
|
-
|
|
127
|
-
operation.process_response(response)
|
|
128
|
-
end
|
|
134
|
+
client.post("/people/batch", data: operation.as_json).tap do |response|
|
|
135
|
+
operation.process_response(response)
|
|
129
136
|
end
|
|
130
137
|
end
|
|
131
138
|
|
|
139
|
+
# @return [Ecoportal::API::Common::Response] the results of the batch job
|
|
132
140
|
def job
|
|
133
141
|
operation = Common::BatchOperation.new("/people", person_class, logger: client.logger)
|
|
134
142
|
yield operation
|
|
135
|
-
# The batch operation is responsible for logging the output
|
|
136
143
|
job_id = create_job(operation)
|
|
137
144
|
status = wait_for_job_completion(job_id)
|
|
138
145
|
|
|
139
146
|
if status&.complete?
|
|
140
|
-
|
|
147
|
+
job_result(job_id, operation)
|
|
141
148
|
else
|
|
142
|
-
|
|
149
|
+
msg = "Job `#{job_id}` not complete. Probably timeout after #{JOB_TIMEOUT} seconds. Current status: #{status}"
|
|
150
|
+
raise API::Errors::TimeOut.new msg
|
|
143
151
|
end
|
|
144
152
|
end
|
|
145
153
|
|
|
@@ -154,8 +162,8 @@ module Ecoportal
|
|
|
154
162
|
JobStatus = Struct.new(:id, :complete?, :errored?, :progress)
|
|
155
163
|
def job_status(job_id)
|
|
156
164
|
response = client.get("/people/job/#{CGI.escape(job_id)}/status")
|
|
157
|
-
body = body_data(response.body)
|
|
158
|
-
raise "Status error" unless response.success?
|
|
165
|
+
body = response && body_data(response.body)
|
|
166
|
+
raise "Status error (#{response.status}) - Errors: #{body}" unless response.success?
|
|
159
167
|
JobStatus.new(
|
|
160
168
|
body["id"],
|
|
161
169
|
body["complete"],
|
|
@@ -164,12 +172,10 @@ module Ecoportal
|
|
|
164
172
|
)
|
|
165
173
|
end
|
|
166
174
|
|
|
175
|
+
# @return [Ecoportal::API::Common::Response] the results of the batch job
|
|
167
176
|
def job_result(job_id, operation)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
client.get("/people/job/#{CGI.escape(job_id)}").tap do |response|
|
|
171
|
-
operation.process_response(response)
|
|
172
|
-
end
|
|
177
|
+
client.get("/people/job/#{CGI.escape(job_id)}").tap do |response|
|
|
178
|
+
operation.process_response(response)
|
|
173
179
|
end
|
|
174
180
|
end
|
|
175
181
|
|
|
@@ -186,12 +192,12 @@ module Ecoportal
|
|
|
186
192
|
end
|
|
187
193
|
end
|
|
188
194
|
|
|
195
|
+
# @return [String] the `id` of the created batch job
|
|
189
196
|
def create_job(operation)
|
|
190
197
|
job_id = nil
|
|
191
|
-
client.
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
end
|
|
198
|
+
client.post("/people/job", data: operation.as_json).tap do |response|
|
|
199
|
+
job_id = body_data(response.body)["id"] if response.success?
|
|
200
|
+
raise "Could not create job - Error (#{response.status}): #{body_data(response.body)}" unless job_id
|
|
195
201
|
end
|
|
196
202
|
job_id
|
|
197
203
|
end
|
|
@@ -64,12 +64,8 @@ module Ecoportal
|
|
|
64
64
|
raise "Invalid filter tag #{tag.inspect}"
|
|
65
65
|
end
|
|
66
66
|
tag.upcase
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
ini_tags = (original_doc && original_doc["filter_tags"]) || []
|
|
70
|
-
# preserve original order to avoid false updates
|
|
71
|
-
doc["filter_tags"] = (ini_tags & end_tags) + (end_tags - ini_tags)
|
|
72
|
-
doc["filter_tags"].compact
|
|
67
|
+
end
|
|
68
|
+
set_uniq_array_keep_order("filter_tags", end_tags)
|
|
73
69
|
end
|
|
74
70
|
|
|
75
71
|
# @return [Array<String>] the filter tags of this person.
|
|
@@ -81,6 +77,10 @@ module Ecoportal
|
|
|
81
77
|
super.merge "details" => details&.as_json
|
|
82
78
|
end
|
|
83
79
|
|
|
80
|
+
def as_update(ref = :last, ignore: [])
|
|
81
|
+
super(ignore: ignore | ["subordinates"])
|
|
82
|
+
end
|
|
83
|
+
|
|
84
84
|
# Sets the PersonDetails to the person, depending on the paramter received:
|
|
85
85
|
# - `nil`: blanks the details.
|
|
86
86
|
# - `PersonDetails`: sets a copy of the object param as details.
|
|
@@ -2,6 +2,9 @@ module Ecoportal
|
|
|
2
2
|
module API
|
|
3
3
|
class V1
|
|
4
4
|
class PersonDetails < Common::BaseModel
|
|
5
|
+
class MissingId < StandardError
|
|
6
|
+
end
|
|
7
|
+
|
|
5
8
|
passthrough :schema_id
|
|
6
9
|
|
|
7
10
|
class_resolver :schema_field_value_class, "Ecoportal::API::V1::SchemaFieldValue"
|
|
@@ -43,16 +46,24 @@ module Ecoportal
|
|
|
43
46
|
end
|
|
44
47
|
|
|
45
48
|
# Sets the value to one specific field of the PersonDetails.
|
|
49
|
+
# @raise MisssingId if the `id` or `alt_id` is missing.
|
|
46
50
|
# @param id [String] the `id` or the `alt_id` of the target field.
|
|
47
51
|
# @return [void]
|
|
48
52
|
def []=(id, value)
|
|
49
53
|
if field = get_field(id)
|
|
50
54
|
field.value = value
|
|
51
55
|
else
|
|
52
|
-
raise "details[#{id.inspect}] is missing. Did you forget to load the schema?"
|
|
56
|
+
raise MissingId.new("details[#{id.inspect}] is missing. Did you forget to load the schema?")
|
|
53
57
|
end
|
|
54
58
|
end
|
|
55
59
|
|
|
60
|
+
# Checks if an `id` or `alt_id` exists
|
|
61
|
+
# @param id [String] the `id` or the `alt_id` of the target field.
|
|
62
|
+
# @return [Boolean] `true` if it exists, `false` otherwise
|
|
63
|
+
def key?(id)
|
|
64
|
+
@fields_by_id.key?(id) || @fields_by_alt_id.key?(id)
|
|
65
|
+
end
|
|
66
|
+
|
|
56
67
|
protected
|
|
57
68
|
|
|
58
69
|
# Rebuilds the internal `id` and `alt_id` references to the fields.
|
|
@@ -6,25 +6,24 @@ module Ecoportal
|
|
|
6
6
|
passthrough :id, :alt_id, :type, :name, :shared, :multiple
|
|
7
7
|
|
|
8
8
|
def value
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
end
|
|
9
|
+
case type
|
|
10
|
+
when "text", "phone_number", "number", "boolean", "select"
|
|
11
|
+
doc["value"]
|
|
12
|
+
when "date"
|
|
13
|
+
if doc["value"]
|
|
14
|
+
maybe_multiple(doc["value"]) do |v|
|
|
15
|
+
Date.iso8601(v)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
else
|
|
19
|
+
raise "Unknown type #{type}"
|
|
20
|
+
end
|
|
22
21
|
end
|
|
23
22
|
|
|
24
23
|
def value=(value)
|
|
25
24
|
case type
|
|
26
25
|
when "text", "phone_number", "select"
|
|
27
|
-
doc["value"] =
|
|
26
|
+
doc["value"] = maybe_multiple(value) do |v|
|
|
28
27
|
v&.to_s
|
|
29
28
|
end
|
|
30
29
|
when "number"
|
|
@@ -33,23 +32,21 @@ module Ecoportal
|
|
|
33
32
|
raise "Invalid number type #{v.class}"
|
|
34
33
|
end
|
|
35
34
|
end
|
|
36
|
-
doc["value"] =
|
|
35
|
+
doc["value"] = value
|
|
37
36
|
when "boolean"
|
|
38
|
-
doc["value"] =
|
|
37
|
+
doc["value"] = !!value
|
|
39
38
|
when "date"
|
|
40
39
|
maybe_multiple(value) do |v|
|
|
41
40
|
unless v.nil? || v.respond_to?(:to_date)
|
|
42
41
|
raise "Invalid date type #{v.class}"
|
|
43
42
|
end
|
|
44
43
|
end
|
|
45
|
-
|
|
46
|
-
doc["value"] = maybe_multiple(@value) do |v|
|
|
44
|
+
doc["value"] = maybe_multiple(value) do |v|
|
|
47
45
|
v&.to_date&.to_s
|
|
48
46
|
end
|
|
49
47
|
else
|
|
50
48
|
raise "Unknown type #{type}"
|
|
51
49
|
end
|
|
52
|
-
@value
|
|
53
50
|
end
|
|
54
51
|
|
|
55
52
|
def maybe_multiple(value)
|
data/lib/ecoportal/api.rb
CHANGED
|
@@ -2,6 +2,7 @@ require "cgi"
|
|
|
2
2
|
require "logger"
|
|
3
3
|
require "hash-polyfill"
|
|
4
4
|
require "ecoportal/api/version"
|
|
5
|
+
require "dotenv/load"
|
|
5
6
|
|
|
6
7
|
module Ecoportal
|
|
7
8
|
module API
|
|
@@ -10,5 +11,6 @@ end
|
|
|
10
11
|
|
|
11
12
|
require "ecoportal/api/logger"
|
|
12
13
|
require "ecoportal/api/common"
|
|
14
|
+
require "ecoportal/api/errors"
|
|
13
15
|
require "ecoportal/api/v1"
|
|
14
16
|
require "ecoportal/api/internal"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ecoportal-api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tapio Saarinen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-03-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -16,7 +16,7 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 2.2.
|
|
19
|
+
version: 2.2.17
|
|
20
20
|
- - "<"
|
|
21
21
|
- !ruby/object:Gem::Version
|
|
22
22
|
version: '2.3'
|
|
@@ -26,7 +26,7 @@ dependencies:
|
|
|
26
26
|
requirements:
|
|
27
27
|
- - ">="
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
|
-
version: 2.2.
|
|
29
|
+
version: 2.2.17
|
|
30
30
|
- - "<"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
32
|
version: '2.3'
|
|
@@ -144,6 +144,46 @@ dependencies:
|
|
|
144
144
|
- - "<"
|
|
145
145
|
- !ruby/object:Gem::Version
|
|
146
146
|
version: '5'
|
|
147
|
+
- !ruby/object:Gem::Dependency
|
|
148
|
+
name: dotenv
|
|
149
|
+
requirement: !ruby/object:Gem::Requirement
|
|
150
|
+
requirements:
|
|
151
|
+
- - ">="
|
|
152
|
+
- !ruby/object:Gem::Version
|
|
153
|
+
version: 2.7.6
|
|
154
|
+
- - "<"
|
|
155
|
+
- !ruby/object:Gem::Version
|
|
156
|
+
version: '2.8'
|
|
157
|
+
type: :runtime
|
|
158
|
+
prerelease: false
|
|
159
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
160
|
+
requirements:
|
|
161
|
+
- - ">="
|
|
162
|
+
- !ruby/object:Gem::Version
|
|
163
|
+
version: 2.7.6
|
|
164
|
+
- - "<"
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '2.8'
|
|
167
|
+
- !ruby/object:Gem::Dependency
|
|
168
|
+
name: elastic-apm
|
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
|
170
|
+
requirements:
|
|
171
|
+
- - ">="
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: 4.0.0
|
|
174
|
+
- - "<"
|
|
175
|
+
- !ruby/object:Gem::Version
|
|
176
|
+
version: '4.1'
|
|
177
|
+
type: :runtime
|
|
178
|
+
prerelease: false
|
|
179
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
180
|
+
requirements:
|
|
181
|
+
- - ">="
|
|
182
|
+
- !ruby/object:Gem::Version
|
|
183
|
+
version: 4.0.0
|
|
184
|
+
- - "<"
|
|
185
|
+
- !ruby/object:Gem::Version
|
|
186
|
+
version: '4.1'
|
|
147
187
|
- !ruby/object:Gem::Dependency
|
|
148
188
|
name: hash-polyfill
|
|
149
189
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -189,10 +229,14 @@ files:
|
|
|
189
229
|
- lib/ecoportal/api/common/batch_response.rb
|
|
190
230
|
- lib/ecoportal/api/common/client.rb
|
|
191
231
|
- lib/ecoportal/api/common/doc_helpers.rb
|
|
232
|
+
- lib/ecoportal/api/common/elastic_apm_integration.rb
|
|
192
233
|
- lib/ecoportal/api/common/hash_diff.rb
|
|
193
234
|
- lib/ecoportal/api/common/logging.rb
|
|
194
235
|
- lib/ecoportal/api/common/response.rb
|
|
195
236
|
- lib/ecoportal/api/common/wrapped_response.rb
|
|
237
|
+
- lib/ecoportal/api/errors.rb
|
|
238
|
+
- lib/ecoportal/api/errors/base.rb
|
|
239
|
+
- lib/ecoportal/api/errors/time_out.rb
|
|
196
240
|
- lib/ecoportal/api/internal.rb
|
|
197
241
|
- lib/ecoportal/api/internal/account.rb
|
|
198
242
|
- lib/ecoportal/api/internal/login_provider.rb
|
|
@@ -237,7 +281,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
237
281
|
- !ruby/object:Gem::Version
|
|
238
282
|
version: '0'
|
|
239
283
|
requirements: []
|
|
240
|
-
rubygems_version: 3.
|
|
284
|
+
rubygems_version: 3.3.5
|
|
241
285
|
signing_key:
|
|
242
286
|
specification_version: 4
|
|
243
287
|
summary: A collection of helpers for interacting with the ecoPortal MS's various APIs
|