ecoportal-api-v2 1.1.7 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.markdownlint.json +4 -0
- data/.rubocop.yml +54 -15
- data/.ruby-version +1 -0
- data/CHANGELOG.md +485 -373
- data/ecoportal-api-v2.gemspec +13 -12
- data/lib/ecoportal/api/common/concerns/benchmarkable.rb +47 -34
- data/lib/ecoportal/api/common/concerns/threadable.rb +41 -0
- data/lib/ecoportal/api/common/concerns.rb +1 -0
- data/lib/ecoportal/api/common/content/array_model.rb +85 -79
- data/lib/ecoportal/api/common/content/class_helpers.rb +34 -31
- data/lib/ecoportal/api/common/content/collection_model.rb +77 -65
- data/lib/ecoportal/api/common/content/double_model.rb +105 -87
- data/lib/ecoportal/api/common/content/wrapped_response.rb +11 -11
- data/lib/ecoportal/api/v2/page/component/reference_field.rb +17 -13
- data/lib/ecoportal/api/v2/page/component.rb +67 -68
- data/lib/ecoportal/api/v2/page/components.rb +9 -9
- data/lib/ecoportal/api/v2/page/force.rb +6 -7
- data/lib/ecoportal/api/v2/page/stages.rb +5 -6
- data/lib/ecoportal/api/v2/page.rb +35 -33
- data/lib/ecoportal/api/v2/pages/page_stage.rb +22 -20
- data/lib/ecoportal/api/v2/pages.rb +18 -14
- data/lib/ecoportal/api/v2/people.rb +2 -3
- data/lib/ecoportal/api/v2/registers.rb +28 -13
- data/lib/ecoportal/api/v2/s3/data.rb +27 -0
- data/lib/ecoportal/api/v2/s3/files/batch_upload.rb +110 -0
- data/lib/ecoportal/api/v2/s3/files/poll.rb +82 -0
- data/lib/ecoportal/api/v2/s3/files/poll_status.rb +52 -0
- data/lib/ecoportal/api/v2/s3/files.rb +132 -0
- data/lib/ecoportal/api/v2/s3/upload.rb +154 -0
- data/lib/ecoportal/api/v2/s3.rb +66 -0
- data/lib/ecoportal/api/v2.rb +10 -3
- data/lib/ecoportal/api/v2_version.rb +1 -1
- metadata +53 -54
@@ -6,12 +6,8 @@ module Ecoportal
|
|
6
6
|
# which differs of `attr_*` ruby native class methods because `pass*`
|
7
7
|
# completelly **links** the methods **to a subjacent `Hash` model**
|
8
8
|
class DoubleModel < Common::BaseModel
|
9
|
-
NOT_USED = Common::Content::ClassHelpers::NOT_USED
|
10
|
-
extend Common::Content::ClassHelpers
|
11
|
-
include Common::Content::ModelHelpers
|
12
|
-
|
13
9
|
class UnlinkedModel < StandardError
|
14
|
-
def initialize
|
10
|
+
def initialize(msg = "Something went wrong when linking the document.", from: nil, key: nil)
|
15
11
|
msg += " From: #{from}." if from
|
16
12
|
msg += " key: #{key}." if key
|
17
13
|
super(msg)
|
@@ -21,6 +17,10 @@ module Ecoportal
|
|
21
17
|
class NoKeyMethod < StandardError
|
22
18
|
end
|
23
19
|
|
20
|
+
NOT_USED = Common::Content::ClassHelpers::NOT_USED
|
21
|
+
extend Common::Content::ClassHelpers
|
22
|
+
include Common::Content::ModelHelpers
|
23
|
+
|
24
24
|
class << self
|
25
25
|
attr_reader :key
|
26
26
|
|
@@ -118,7 +118,7 @@ module Ecoportal
|
|
118
118
|
# as well as its `key` in the underlying `Hash` model.
|
119
119
|
# @param default [Value] the default value that
|
120
120
|
# this `key` will be written in the model when it doesn't exixt
|
121
|
-
def passforced(method, default
|
121
|
+
def passforced(method, default:, read_only: false)
|
122
122
|
model_forced_keys[method.to_s.freeze] = default
|
123
123
|
passthrough(method, read_only: read_only)
|
124
124
|
end
|
@@ -126,8 +126,9 @@ module Ecoportal
|
|
126
126
|
# Ensures `doc` has the `model_forced_keys`. If it doesn't, it adds those missing
|
127
127
|
# with the defined `default` values
|
128
128
|
def enforce!(doc)
|
129
|
-
return unless doc
|
130
|
-
return if
|
129
|
+
return unless doc.is_a?(Hash)
|
130
|
+
return if model_forced_keys.empty?
|
131
|
+
|
131
132
|
model_forced_keys.each do |key, default|
|
132
133
|
doc[key] = default unless doc.key?(key)
|
133
134
|
end
|
@@ -139,8 +140,8 @@ module Ecoportal
|
|
139
140
|
# as well as its `key` in the underlying `Hash` model.
|
140
141
|
# @param read_only [Boolean] should it only define the reader?
|
141
142
|
def passthrough(*methods, read_only: false)
|
142
|
-
pass_reader
|
143
|
-
pass_writer
|
143
|
+
pass_reader(*methods)
|
144
|
+
pass_writer(*methods) unless read_only
|
144
145
|
self
|
145
146
|
end
|
146
147
|
|
@@ -151,9 +152,7 @@ module Ecoportal
|
|
151
152
|
# @param read_only [Boolean] should it only define the reader?
|
152
153
|
def passdate(*methods, read_only: false)
|
153
154
|
pass_reader(*methods) {|value| to_time(value)}
|
154
|
-
unless read_only
|
155
|
-
pass_writer(*methods) {|value| to_time(value)&.iso8601}
|
156
|
-
end
|
155
|
+
pass_writer(*methods) {|value| to_time(value)&.iso8601} unless read_only
|
157
156
|
self
|
158
157
|
end
|
159
158
|
|
@@ -163,9 +162,7 @@ module Ecoportal
|
|
163
162
|
# @param read_only [Boolean] should it only define the reader?
|
164
163
|
def passboolean(*methods, read_only: false)
|
165
164
|
pass_reader(*methods) {|value| value}
|
166
|
-
unless read_only
|
167
|
-
pass_writer(*methods) {|value| !!value}
|
168
|
-
end
|
165
|
+
pass_writer(*methods) {|value| !!value} unless read_only
|
169
166
|
self
|
170
167
|
end
|
171
168
|
|
@@ -186,7 +183,7 @@ module Ecoportal
|
|
186
183
|
|
187
184
|
define_method method do
|
188
185
|
return instance_variable_get(var) if instance_variable_defined?(var)
|
189
|
-
new_obj = dim_class.new(parent: self, key: method, read_only:
|
186
|
+
new_obj = dim_class.new(parent: self, key: method, read_only: read_only?)
|
190
187
|
variable_set(var, new_obj)
|
191
188
|
end
|
192
189
|
end
|
@@ -197,7 +194,7 @@ module Ecoportal
|
|
197
194
|
# @param key [Symbol] the `key` that embeds it to the underlying `Hash` model
|
198
195
|
# @nullable [Boolean] to specify if this object can be `nil`
|
199
196
|
# @param klass [Class, String] the class of the embedded object
|
200
|
-
def embeds_one(method, key: method, nullable: false
|
197
|
+
def embeds_one(method, klass:, key: method, nullable: false)
|
201
198
|
embed(method, key: key, nullable: nullable, multiple: false, klass: klass)
|
202
199
|
end
|
203
200
|
|
@@ -212,12 +209,13 @@ module Ecoportal
|
|
212
209
|
# @param read_only [Boolean] whether or not should try to **work around** items `klass` missing a `key`
|
213
210
|
# - If set to `true` this is meant only for read purposes (won't be able to successufully insert)
|
214
211
|
def embeds_many(method, key: method, klass: nil, enum_class: nil,
|
215
|
-
order_matters: false, order_key: nil, read_only:
|
212
|
+
order_matters: false, order_key: nil, read_only: read_only?)
|
216
213
|
if enum_class
|
217
214
|
eclass = enum_class
|
218
215
|
elsif klass
|
219
216
|
eclass = new_class("#{method}::#{klass}", inherits: Common::Content::CollectionModel) do |dim_class|
|
220
|
-
|
217
|
+
# NOTE: new_class may resolve the namespace of the class to an already existing class
|
218
|
+
dim_class.klass ||= klass
|
221
219
|
dim_class.order_matters = order_matters
|
222
220
|
dim_class.order_key = order_key
|
223
221
|
dim_class.read_only! if read_only
|
@@ -225,61 +223,69 @@ module Ecoportal
|
|
225
223
|
else
|
226
224
|
raise "You should either specify the 'klass' of the elements or the 'enum_class'"
|
227
225
|
end
|
228
|
-
|
226
|
+
|
227
|
+
embed(
|
228
|
+
method, key: key,
|
229
|
+
multiple: true, klass: eclass,
|
230
|
+
read_only: read_only
|
231
|
+
) do |instance_with_called_method|
|
229
232
|
# keep reference to the original class to resolve the `klass` dependency
|
230
233
|
# See stackoverflow: https://stackoverflow.com/a/73709529/4352306
|
231
234
|
referrer_class = instance_with_called_method.class
|
232
|
-
eclass.klass
|
235
|
+
eclass.klass = {referrer_class => klass} if klass
|
236
|
+
# This helps `resolve_class` to correctly resolve a symbol
|
237
|
+
# by using referrer_class as a base module to resolve it
|
233
238
|
end
|
234
239
|
end
|
235
240
|
|
236
241
|
private
|
237
242
|
|
238
|
-
def embed(
|
243
|
+
def embed(
|
244
|
+
method, klass:, key: method,
|
245
|
+
nullable: false, multiple: false, read_only: read_only?,
|
246
|
+
&embed_block
|
247
|
+
)
|
239
248
|
method = method.to_s.freeze
|
240
249
|
var = instance_variable_name(method).freeze
|
241
|
-
|
250
|
+
obj_k = key.to_s.freeze
|
242
251
|
|
243
252
|
# retrieving method (getter)
|
244
253
|
define_method(method) do
|
245
|
-
|
254
|
+
# set item klass as referrer to klass (to allow resolve symbol)
|
255
|
+
embed_block&.call(self)
|
246
256
|
return instance_variable_get(var) if instance_variable_defined?(var)
|
247
|
-
unless nullable
|
248
|
-
doc[k] ||= multiple ? [] : {}
|
249
|
-
end
|
250
|
-
return variable_set(var, nil) unless doc[k]
|
251
257
|
|
252
|
-
|
253
|
-
|
254
|
-
if multiple && read_only
|
255
|
-
if doc[k].is_a?(Array) && embedded_class < Common::Content::CollectionModel
|
256
|
-
if (item_class = embedded_class.klass) && !item_class.key?
|
257
|
-
item_class.passkey :id
|
258
|
-
doc[k].each_with_index do |item_doc, i|
|
259
|
-
item_doc["id"] = "#{i}" unless item_doc.key?("id")
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|
258
|
+
doc[obj_k] ||= (multiple ? [] : {}) unless nullable
|
259
|
+
return variable_set(var, nil) unless doc[obj_k]
|
264
260
|
|
265
|
-
embedded_class
|
266
|
-
|
261
|
+
embedded_class = self.class.resolve_class(klass)
|
262
|
+
setup_items_key(embedded_class, doc[obj_k]) if multiple && read_only
|
263
|
+
|
264
|
+
embedded_class.new(
|
265
|
+
doc[obj_k],
|
266
|
+
parent: self,
|
267
|
+
key: obj_k,
|
268
|
+
read_only: read_only? || read_only
|
269
|
+
).tap do |collection|
|
270
|
+
variable_set(var, collection)
|
267
271
|
end
|
268
272
|
end
|
269
273
|
end
|
270
274
|
|
271
275
|
# The list of keys that will be forced in the model
|
272
276
|
def model_forced_keys
|
273
|
-
@
|
277
|
+
@model_forced_keys ||= {}
|
274
278
|
end
|
275
279
|
end
|
276
280
|
|
277
|
-
inheritable_class_vars :
|
281
|
+
inheritable_class_vars :model_forced_keys, :key, :read_only
|
278
282
|
|
279
|
-
# `_key` refers to the
|
283
|
+
# `_key` refers to the `_parent`'s property that links to this model
|
284
|
+
# @note while `key` refers to the value of theproperty of this model
|
285
|
+
# that is key (identifies an item in a set of elements)
|
280
286
|
attr_reader :_parent, :_key, :_read_only
|
281
287
|
|
282
|
-
def initialize(doc = {}, parent: self, key: nil, read_only: self.class.read_only?)
|
288
|
+
def initialize(doc = {}, parent: self, key: nil, read_only: self.class.read_only?) # rubocop:disable Lint/MissingSuper
|
283
289
|
@_dim_vars = []
|
284
290
|
@_parent = parent || self
|
285
291
|
@_key = key || self
|
@@ -292,10 +298,10 @@ module Ecoportal
|
|
292
298
|
@original_doc = JSON.parse(@doc.to_json)
|
293
299
|
end
|
294
300
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
301
|
+
return unless key_method? && doc && doc.is_a?(Hash)
|
302
|
+
|
303
|
+
self.key = doc[key_method]
|
304
|
+
#puts "\n$(#{self.key}<=>#{self.class})"
|
299
305
|
end
|
300
306
|
|
301
307
|
# @note `read_only` allows for some optimizations, such as storing values
|
@@ -311,15 +317,20 @@ module Ecoportal
|
|
311
317
|
|
312
318
|
# @return [String] the `value` of the `key` method (i.e. `id` value)
|
313
319
|
def key
|
314
|
-
raise NoKeyMethod
|
315
|
-
|
320
|
+
raise NoKeyMethod, "No key_method defined for #{self.class}" unless key_method?
|
321
|
+
|
322
|
+
method(key_method).call
|
316
323
|
end
|
317
324
|
|
318
325
|
# @param [String] the `value` of the `key` method (i.e. `id` value)
|
319
326
|
def key=(value)
|
320
|
-
raise NoKeyMethod
|
321
|
-
|
322
|
-
|
327
|
+
raise NoKeyMethod, "No key_method defined for #{self.class}" unless key_method?
|
328
|
+
|
329
|
+
method("#{key_method}=").call(value)
|
330
|
+
end
|
331
|
+
|
332
|
+
def resolved_doc_key
|
333
|
+
[_doc_key(_key)]
|
323
334
|
end
|
324
335
|
|
325
336
|
# Offers a method for child classes to transform the key,
|
@@ -337,25 +348,22 @@ module Ecoportal
|
|
337
348
|
# @return [nil, Hash] the underlying `Hash` model as is (carrying current changes)
|
338
349
|
def doc
|
339
350
|
return @doc if doc_var?
|
351
|
+
|
340
352
|
raise UnlinkedModel.new(from: "#{self.class}#doc", key: _key) unless linked?
|
341
|
-
if is_root?
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
_parent.doc.dig(*[_doc_key(_key)].flatten)
|
347
|
-
end
|
353
|
+
return @doc if is_root?
|
354
|
+
|
355
|
+
# transform parent's `_key` to this object into a
|
356
|
+
# path key that can rerieve from the parents's doc
|
357
|
+
_parent.doc.dig(*resolved_doc_key.flatten)
|
348
358
|
end
|
349
359
|
|
350
360
|
# The `original_doc` holds the model as is now on server-side.
|
351
361
|
# @return [nil, Hash] the underlying `Hash` model as after last `consolidate!` changes
|
352
362
|
def original_doc
|
353
363
|
raise UnlinkedModel.new(from: "#{self.class}#original_doc", key: _key) unless linked?
|
354
|
-
if is_root?
|
355
|
-
|
356
|
-
|
357
|
-
_parent.original_doc.dig(*[_doc_key(_key)].flatten)
|
358
|
-
end
|
364
|
+
return @original_doc if is_root?
|
365
|
+
|
366
|
+
_parent.original_doc.dig(*resolved_doc_key.flatten)
|
359
367
|
end
|
360
368
|
|
361
369
|
def as_json
|
@@ -376,7 +384,7 @@ module Ecoportal
|
|
376
384
|
# @return [Boolean] stating if there are changes
|
377
385
|
def dirty?
|
378
386
|
au = as_update
|
379
|
-
!((au == {}) ||
|
387
|
+
!((au == {}) || au.nil?)
|
380
388
|
end
|
381
389
|
|
382
390
|
# It makes `original_doc` to be like `doc`
|
@@ -396,7 +404,7 @@ module Ecoportal
|
|
396
404
|
if key
|
397
405
|
keys = [key].flatten.compact
|
398
406
|
odoc = original_doc.dig(*keys)
|
399
|
-
odoc
|
407
|
+
odoc &&= JSON.parse(odoc.to_json)
|
400
408
|
dig_set(doc, keys, odoc)
|
401
409
|
else
|
402
410
|
replace_doc(JSON.parse(original_doc.to_json))
|
@@ -410,13 +418,11 @@ module Ecoportal
|
|
410
418
|
|
411
419
|
def replace_doc(new_doc)
|
412
420
|
raise UnlinkedModel.new(from: "#{self.class}#replace_doc", key: _key) unless linked?
|
413
|
-
if is_root?
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
#variables_remove!
|
419
|
-
end
|
421
|
+
return (@doc = new_doc) if is_root?
|
422
|
+
|
423
|
+
dig_set(_parent.doc, resolved_doc_key.flatten, new_doc)
|
424
|
+
_parent.variable_remove!(_key) unless new_doc
|
425
|
+
#variables_remove!
|
420
426
|
end
|
421
427
|
|
422
428
|
protected
|
@@ -438,11 +444,9 @@ module Ecoportal
|
|
438
444
|
|
439
445
|
def replace_original_doc(new_doc)
|
440
446
|
raise UnlinkedModel.new(from: "#{self.class}#replace_original_doc", key: _key) unless linked?
|
441
|
-
if is_root?
|
442
|
-
|
443
|
-
|
444
|
-
dig_set(_parent.original_doc, [_doc_key(_key)].flatten, new_doc)
|
445
|
-
end
|
447
|
+
return (@original_doc = new_doc) if is_root?
|
448
|
+
|
449
|
+
dig_set(_parent.original_doc, resolved_doc_key.flatten, new_doc)
|
446
450
|
end
|
447
451
|
|
448
452
|
# Helper to track down persistent variables
|
@@ -455,16 +459,16 @@ module Ecoportal
|
|
455
459
|
# Helper to remove tracked down instance variables
|
456
460
|
def variable_remove!(key)
|
457
461
|
var = instance_variable_name(key)
|
458
|
-
unless
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
+
return unless @_dim_vars.include?(var)
|
463
|
+
|
464
|
+
@_dim_vars.delete(var)
|
465
|
+
remove_instance_variable(var)
|
462
466
|
end
|
463
467
|
|
464
468
|
# Removes all the persistent variables
|
465
469
|
def variables_remove!
|
466
470
|
#puts "going to remove vars: #{@_dim_vars} on #{self.class} (parent: #{identify_parent(self._parent)})"
|
467
|
-
@_dim_vars.dup.map {|
|
471
|
+
@_dim_vars.dup.map {|var| variable_remove!(var)}
|
468
472
|
end
|
469
473
|
|
470
474
|
private
|
@@ -502,6 +506,20 @@ module Ecoportal
|
|
502
506
|
self.class.key
|
503
507
|
end
|
504
508
|
|
509
|
+
# It allows to work-around missing item_key
|
510
|
+
def setup_items_key(embedded_class, obj_doc)
|
511
|
+
# only if is going to be a collection
|
512
|
+
return unless obj_doc.is_a?(Array) && embedded_class < Common::Content::CollectionModel
|
513
|
+
return unless (item_class = embedded_class.klass)
|
514
|
+
# if already has key don't need to work around
|
515
|
+
return if item_class&.key
|
516
|
+
|
517
|
+
# apply work around
|
518
|
+
item_class.passkey :id
|
519
|
+
obj_doc.each_with_index do |item_doc, idx|
|
520
|
+
item_doc["id"] = idx.to_s unless item_doc.key?("id")
|
521
|
+
end
|
522
|
+
end
|
505
523
|
end
|
506
524
|
end
|
507
525
|
end
|
@@ -7,25 +7,26 @@ module Ecoportal
|
|
7
7
|
|
8
8
|
attr_reader :response, :result
|
9
9
|
|
10
|
-
def initialize(response, klass, key: nil)
|
10
|
+
def initialize(response, klass, key: nil) # rubocop:disable Lint/MissingSuper
|
11
11
|
@response = response
|
12
12
|
@klass = klass
|
13
13
|
@key = key
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
else
|
22
|
-
@klass.new(data)
|
15
|
+
return unless @response.success?
|
16
|
+
|
17
|
+
@result =
|
18
|
+
if data.is_a?(Array)
|
19
|
+
data.map do |doc|
|
20
|
+
@klass.new(doc)
|
23
21
|
end
|
24
|
-
|
22
|
+
else
|
23
|
+
@klass.new(data)
|
24
|
+
end
|
25
25
|
end
|
26
26
|
|
27
27
|
def data
|
28
28
|
return @data if instance_variable_defined?(:@data)
|
29
|
+
|
29
30
|
@data = (response.body || {})["data"]
|
30
31
|
@data = @data[@key] if @key && @data
|
31
32
|
@data
|
@@ -34,7 +35,6 @@ module Ecoportal
|
|
34
35
|
def body
|
35
36
|
data.to_s
|
36
37
|
end
|
37
|
-
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -25,8 +25,8 @@ module Ecoportal
|
|
25
25
|
ref_ids.each do |ref_id|
|
26
26
|
next if reference_ids.include?(ref_id)
|
27
27
|
refs.push({
|
28
|
-
"id"
|
29
|
-
"weight"
|
28
|
+
"id" => ref_id,
|
29
|
+
"weight" => 0,
|
30
30
|
"patch_ver" => 0
|
31
31
|
})
|
32
32
|
end
|
@@ -39,9 +39,13 @@ module Ecoportal
|
|
39
39
|
|
40
40
|
def delete(*ref_ids)
|
41
41
|
ref_ids.each do |ref_id|
|
42
|
-
|
43
|
-
|
42
|
+
doc_ref = doc["references"].find do |df|
|
43
|
+
df["id"] == ref_id
|
44
44
|
end
|
45
|
+
|
46
|
+
next unless doc_ref
|
47
|
+
|
48
|
+
doc["references"].delete(doc_ref)
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
@@ -62,21 +66,21 @@ module Ecoportal
|
|
62
66
|
self.display_fields = true
|
63
67
|
self.display_fields_in_lookup = true
|
64
68
|
when Hash
|
65
|
-
supported = [
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
supported = %i[create attach metadata]
|
70
|
+
rest = hash_except(cnf.dup, *supported)
|
71
|
+
|
72
|
+
unused.push(rest) unless rest.empty?
|
73
|
+
|
74
|
+
self.hide_create = !cnf[:create] if cnf.key?(:create)
|
75
|
+
self.hide_attach = !cnf[:attach] if cnf.key?(:attach)
|
76
|
+
self.hide_metadata = !cnf[:metadata] if cnf.key?(:metadata)
|
72
77
|
else
|
73
78
|
unused.push(cnf)
|
74
79
|
end
|
75
|
-
end.
|
80
|
+
end.then do |unused|
|
76
81
|
super(*unused)
|
77
82
|
end
|
78
83
|
end
|
79
|
-
|
80
84
|
end
|
81
85
|
end
|
82
86
|
end
|