eco-helpers 3.2.3 → 3.2.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3323b0394649126c76ec4239eade6cca337049bd8f761f96aa7c66034717bd0
4
- data.tar.gz: 3ddedb00b3b1e7674e014143e54e9cb46e52abb9482e9dfbc65dd4059fd0a70e
3
+ metadata.gz: ec05b98fd01a73712e5db28ef750583f57d96438f30a2e4e58fc531d007802e1
4
+ data.tar.gz: c1071fd1909830cfbb72838f9051234f9268a5bafa6e6b970a80790c05ef1ad5
5
5
  SHA512:
6
- metadata.gz: 6b6126ec90b13ef1d2d985b384215a529b5818ff5d8139d96fe80d5c21c6a4a0b367cb9bb7bf8b92ff6ed61f3df525f35e2639b5160693cc0e2fd6109bfb02cb
7
- data.tar.gz: d4b799b66943fda1cf7340f358520d1f30715e27866ba900615cedc5bab593200a53cb2ef72bb6ff04c8e8b4e7c4b1b6bd1b0240145240364f67f633fbb3fbd6
6
+ metadata.gz: 779aa495731d5e3abcb145c983c6a8271e9d4a41fb939f4a63dda4735b08b0dfc7fd2df1fcafbfade6e65651fa3af24c27b94cf18ea6977d752ef8c36abbb1da
7
+ data.tar.gz: edc3abd001e262f4ed9bab3867bcd9e8f4d9de108ae58b8a16f581bf054e546908c175d1bd24f8541977f65b44972226cae0b1b58ef20ad112cb2b90af591c1c
data/CHANGELOG.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [3.2.4] - 2025-08-xx
5
+ ## [3.2.5] - 2025-08-xx
6
6
 
7
7
  ### Added
8
8
 
@@ -10,6 +10,14 @@ All notable changes to this project will be documented in this file.
10
10
 
11
11
  ### Fixed
12
12
 
13
+ ## [3.2.4] - 2025-08-26
14
+
15
+ ### Fixed
16
+
17
+ - upgraded `ecoportal-api-graphql` **gem**
18
+ - **DRAFTING**: made it so we don't fetch structure but when needed.
19
+ - This adjustment significantly mitigates performance downfall.
20
+
13
21
  ## [3.2.3] - 2025-08-21
14
22
 
15
23
  ### Added
data/eco-helpers.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.required_ruby_version = '>= 3.2.2'
21
21
 
22
22
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
23
- f.match(%r{^(test|spec|features)/})
23
+ f.match(/^(test|spec|features)\//)
24
24
  end
25
25
 
26
26
  #spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -42,7 +42,7 @@ Gem::Specification.new do |spec|
42
42
  spec.add_dependency 'docx', '>= 0.8.0', '< 0.9'
43
43
  spec.add_dependency 'dotenv', '~> 3'
44
44
  spec.add_dependency 'ecoportal-api', '~> 0.10', '>= 0.10.14'
45
- spec.add_dependency 'ecoportal-api-graphql', '~> 1.3', '>= 1.3.2'
45
+ spec.add_dependency 'ecoportal-api-graphql', '~> 1.3', '>= 1.3.3'
46
46
  spec.add_dependency 'ecoportal-api-v2', '~> 3.3', '>= 3.3.1'
47
47
  spec.add_dependency 'ed25519', '~> 1.2'
48
48
  spec.add_dependency 'fast_excel', '>= 0.5.0', '< 0.6'
@@ -2,9 +2,11 @@ class Eco::API::UseCases::GraphQL::Helpers::Location::Command::Diffs
2
2
  module Stages
3
3
  module DiffSortable
4
4
  module ForUnarchive
5
- # we require the original tree to be able to track what ancestors
5
+ # We require the original tree to be able to track what ancestors
6
6
  # should be unarchived, with the aim of unarchiving a node that
7
- # is target of update (i.e. revive and move somewhere else)
7
+ # is target of update (i.e. revive and move somewhere else).
8
+ # @note it is **IMPORTANT** to note, that only the original tree
9
+ # is required. NO need to track the intermediate tree.
8
10
  def original_tree
9
11
  defined?(super)? super : @original_tree
10
12
  end
@@ -7,6 +7,7 @@ class Eco::API::UseCases::GraphQL::Helpers::Location::Command::Diffs
7
7
  class << self
8
8
  def included(base)
9
9
  super
10
+
10
11
  base.send(:include, Sortable)
11
12
  end
12
13
  end
@@ -33,11 +33,13 @@ class Eco::API::UseCases::GraphQL::Helpers::Location::Command::Diffs
33
33
  pending = parents_hash.dup
34
34
  treeify = proc do |item|
35
35
  next item unless (children = pending.delete(item))
36
+
36
37
  {item => children.map {|child| treeify.call(child)}}
37
38
  end
38
39
 
39
40
  sorted_tree = top_parents.each_with_object(unpaired) do |item, tree|
40
41
  next unless pending.key?(item)
42
+
41
43
  tree << treeify.call(item)
42
44
  end
43
45
 
@@ -87,10 +89,12 @@ class Eco::API::UseCases::GraphQL::Helpers::Location::Command::Diffs
87
89
  def paired_related_nodes
88
90
  paired_combinations.each_with_object([]) do |pair, chained|
89
91
  next if (result = compare_two(*pair)).zero?
92
+
90
93
  chained << (result.negative?? pair : pair.reverse)
91
94
  end
92
95
  end
93
96
 
97
+ # @note non optimal... that's why `clusterer_class` would be used instead.
94
98
  def paired_combinations
95
99
  list.combination(2).to_a
96
100
  end
@@ -9,6 +9,7 @@ class Eco::API::UseCases::GraphQL::Helpers::Location::Command::Diffs
9
9
  class << self
10
10
  def included(base)
11
11
  super
12
+
12
13
  base.extend(Stages)
13
14
  end
14
15
  end
@@ -16,7 +17,11 @@ class Eco::API::UseCases::GraphQL::Helpers::Location::Command::Diffs
16
17
  private
17
18
 
18
19
  def diffs_sort(list, cluster_mode: nil, &block)
19
- RelationSafeSort.new(list, cluster_mode: cluster_mode, &block).sort
20
+ RelationSafeSort.new(
21
+ list,
22
+ cluster_mode: cluster_mode,
23
+ &block
24
+ ).sort
20
25
  end
21
26
 
22
27
  # @note As can't update archived nodes, it happens before :id update (:prev),
@@ -3,32 +3,6 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
3
3
  module Optimizations
4
4
  DEFAULT_COMMANDS_PER_PAGE = 45
5
5
 
6
- # Available options are:
7
- # - :per_request -> on each request
8
- # - :per_batch -> at the end of each batch / stage (on last page)
9
- # - :once -> only when the script starts to run
10
- # @return [Symbol] the default tree tracking mode
11
- def default_tree_tracking_mode
12
- :per_request # :per_batch @todo per batch/stage is more plausible
13
- end
14
-
15
- # Helper to identify the commands payload block
16
- # @note
17
- # 1. Gives flexibility on at what time the structure should be retrieved
18
- # 2. This increases performacne, as we don't retrieve the full tree on
19
- # each request unless necessary/specified
20
- # 3. `nil` falls the block back to the `ecoportal-api-graphql` gem, which
21
- # has a default block that retrieves the structure
22
- # @return [Proc, NilClass] the payload block
23
- def scope_commands_block(track_tree_mode: default_tree_tracking_mode)
24
- case track_tree_mode
25
- when :per_request
26
- nil
27
- when :once, :per_batch
28
- commands_payload_without_structure_block
29
- end
30
- end
31
-
32
6
  # Prevents each request from timing out
33
7
  def commands_per_page
34
8
  if self.class.const_defined?(:COMMANDS_PER_PAGE)
@@ -59,6 +33,47 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
59
33
  }
60
34
  }
61
35
  end
36
+
37
+ def create_payload_without_structure_block
38
+ proc {
39
+ # work-around:
40
+ clientMutationId
41
+ errors { # rubocop:disable Style/BlockDelimiters
42
+ details
43
+ fullMessages
44
+ messages
45
+ }
46
+ draft { # rubocop:disable Style/BlockDelimiters
47
+ ___Ecoportal__API__GraphQL__Fragment__LocationDraft
48
+ }
49
+ }
50
+ end
51
+
52
+ def publish_payload_without_structure_block
53
+ proc {
54
+ clientMutationId
55
+ ok
56
+ error { # rubocop:disable Style/BlockDelimiters
57
+ ___Ecoportal__API__GraphQL__Fragment__LocationsError
58
+ }
59
+ errors { # rubocop:disable Style/BlockDelimiters
60
+ details
61
+ fullMessages
62
+ messages
63
+ }
64
+ results { # rubocop:disable Style/BlockDelimiters
65
+ ok
66
+ command { # rubocop:disable Style/BlockDelimiters
67
+ id
68
+ state
69
+ __typename
70
+ }
71
+ error { # rubocop:disable Style/BlockDelimiters
72
+ ___Ecoportal__API__GraphQL__Fragment__LocationsError
73
+ }
74
+ }
75
+ }
76
+ end
62
77
  end
63
78
  end
64
79
  end
@@ -7,21 +7,33 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
7
7
 
8
8
  include Optimizations
9
9
 
10
+ def fetch_draft(
11
+ draft_id,
12
+ structure_id:,
13
+ &block
14
+ )
15
+ log(:info) { "Going to fetch draft #{draft_id} (for #{structure_id})..." }
16
+
17
+ graphql.currentOrganization.locationDraftById(
18
+ id: draft_id,
19
+ structureId: structure_id,
20
+ &block
21
+ )
22
+ end
23
+
10
24
  def create_draft(
11
25
  structure_id,
12
- name: Time.now.iso8601,
13
- notes: '',
14
- include_archived_nodes: true
26
+ name: Time.now.iso8601,
27
+ notes: ''
15
28
  )
16
29
  log(:info) { "Going to create draft (for #{structure_id})..." }
17
30
 
18
31
  graphql.locationStructure.draft.create(
19
- input: {
32
+ input: {
20
33
  structureId: structure_id,
21
34
  name: name,
22
35
  notes: notes
23
- },
24
- includeArchivedNodes: include_archived_nodes
36
+ }
25
37
  ).tap do |payload|
26
38
  unless payload.error?
27
39
  log(:info) { "Created draft '#{payload.draft&.id}' (for #{structure_id})" }
@@ -55,19 +67,17 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
55
67
 
56
68
  def publish_draft(
57
69
  draft_id,
58
- preview: false,
59
- force: false,
60
- include_archived_nodes: true
70
+ preview: false,
71
+ force: false
61
72
  )
62
73
  log(:info) { "Going to publish draft #{draft_id}..." }
63
74
 
64
75
  graphql.locationStructure.draft.publish(
65
- input: {
76
+ input: {
66
77
  id: draft_id,
67
78
  preview: preview,
68
79
  force: force
69
- },
70
- includeArchivedNodes: include_archived_nodes
80
+ }
71
81
  ).tap do |payload|
72
82
  unless payload.error?
73
83
  log(:info) { "Published draft #{draft_id}" }
@@ -81,16 +91,11 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
81
91
  end
82
92
  end
83
93
 
84
- def apply_commands(
85
- input,
86
- final: false,
87
- track_tree_mode: nil,
88
- &block
89
- )
90
- block ||= scope_commands_block(track_tree_mode: track_tree_mode)
91
- block = nil if final
92
-
93
- graphql.locationStructure.draft.addCommands(input: input, &block)
94
+ def add_commands(input, &block)
95
+ graphql.locationStructure.draft.addCommands(
96
+ input: input,
97
+ &block
98
+ )
94
99
  end
95
100
  end
96
101
  end
@@ -2,18 +2,23 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
2
2
  # Final ressults of the full run.
3
3
  class Results
4
4
  attr_accessor :draft
5
+ attr_accessor :target_tree
6
+
7
+ def initialize(target_tree)
8
+ @target_tree = target_tree
9
+ end
5
10
 
6
11
  def draft_id
7
12
  draft&.id
8
13
  end
9
14
 
10
15
  def parent_structure
11
- draft&.parent_structure
16
+ draft&.parent_structure || target_tree
12
17
  end
13
18
 
14
19
  # @note target parent structure id
15
20
  def structure_id
16
- draft&.structure_id
21
+ parent_structure&.id
17
22
  end
18
23
 
19
24
  # @note the result of the draft
@@ -160,7 +165,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
160
165
  end
161
166
 
162
167
  def some_applied?
163
- applied.count.positive?
168
+ applied.any?
164
169
  end
165
170
 
166
171
  def last_applied
@@ -7,13 +7,11 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
7
7
  include Eco::API::UseCases::GraphQL::Helpers::Location::Command::EndPoints
8
8
 
9
9
  # @note it does the API call.
10
- # @param track_tree_mode [Symbol] when should updates happen
11
10
  # @return see #with_sliced_input
12
11
  def sliced_batches(
13
12
  batch_input,
14
- size: commands_per_page,
15
- desc: :input,
16
- track_tree_mode: default_tree_tracking_mode
13
+ size: commands_per_page,
14
+ desc: :input
17
15
  )
18
16
  dry_run_msg = simulate? ? '(dry-run) ' : ''
19
17
 
@@ -32,10 +30,9 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
32
30
  log(:info) { msg }
33
31
 
34
32
  unless simulate? && !options.dig(:requests, :backup)
35
- backup(
36
- sliced_input,
37
- type: "tree_update_#{desc}_request_#{page}_of_#{pages}"
38
- )
33
+ watermark = "tree_update_#{desc}_response_#{page}_of_#{pages}"
34
+
35
+ backup(sliced_input, type: watermark)
39
36
  end
40
37
 
41
38
  if simulate? && page < 3
@@ -45,18 +42,15 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
45
42
  end
46
43
 
47
44
  # fetch structure? (by using default, when curr_block == nil)
48
- response = apply_commands(
45
+ # We should only fetch at the very end (last standard stage)
46
+ # - We will explicitly fetch the draft structure before re-archive.
47
+ response = add_commands(
49
48
  sliced_input,
50
- final: (page == pages),
51
- track_tree_mode: track_tree_mode
49
+ &commands_payload_without_structure_block
52
50
  )
53
51
 
54
- unless simulate?
55
- backup(
56
- response,
57
- type: "tree_update_#{desc}_response_#{page}_of_#{pages}"
58
- )
59
- end
52
+ watermark = "tree_update_#{desc}_response_#{page}_of_#{pages}"
53
+ backup(response, type: watermark) unless simulate?
60
54
 
61
55
  done += count
62
56
  yield(sliced_input, response, page, pages, done, total) if block_given?
@@ -9,34 +9,15 @@ class Eco::API::UseCases::GraphQL::Samples::Location
9
9
  false
10
10
  end
11
11
 
12
- # Default tree tacking behaviour
13
- # @note
14
- # 1. This aims to optimize the time run
15
- # 2. Based on update stage, there are differentiated tracking needs
16
- # @note Available options are:
17
- # - :per_request -> on each request
18
- # - :per_batch -> at the end of each batch / stage (on last page)
19
- # - :once -> only when the script starts to run
20
- # @return [Symbol] the tracking mode
21
- def batch_tree_track_mode(stage)
22
- case stage
23
- when :unarchive, :archive
24
- :once
25
- when :id, :id_name
26
- :per_request
27
- when :insert, :move
28
- :per_batch
29
- else
30
- default_tree_tracking_mode
31
- end
32
- end
33
-
34
12
  # With given the commands, it generates the input of the endpoint mutation.
35
13
  # @param commands [Array<Hash>]
36
14
  def input(commands, force_continue: force_continue?)
37
15
  return unless (commands || []).any?
38
16
 
39
- results.draft ||= create_draft(current_tree.id).draft
17
+ results.draft ||= create_draft(
18
+ current_tree.id,
19
+ &create_payload_without_structure_block
20
+ ).draft
40
21
 
41
22
  {
42
23
  clientMutationId: '',
@@ -79,23 +60,27 @@ class Eco::API::UseCases::GraphQL::Samples::Location
79
60
 
80
61
  # MAIN PROCESSOR
81
62
  def process
82
- self.error = false
63
+ self.error = false
64
+ some_update = false
83
65
 
84
66
  with_error_handling do
85
67
  super if defined?(super)
86
68
 
87
- # this may trigger a backup of the tagtree
69
+ # The very first track of the current structure.
70
+ # - This may trigger a backup of the tagtree.
88
71
  self.current_tree ||= live_tree
72
+ results(current_tree)
89
73
 
90
74
  inputs(
91
75
  force_continue: force_continue?
92
76
  ) do |input, stage|
93
77
  next unless input
94
78
 
79
+ some_update = true
80
+
95
81
  sliced_batches(
96
82
  input,
97
- desc: stage,
98
- track_tree_mode: batch_tree_track_mode(stage),
83
+ desc: stage,
99
84
  &results_tracking_block(stage: stage)
100
85
  )
101
86
 
@@ -106,7 +91,13 @@ class Eco::API::UseCases::GraphQL::Samples::Location
106
91
  end
107
92
  end
108
93
  ensure
109
- rescued { rearchive } unless exception
94
+ rescued do
95
+ next if exception
96
+
97
+ refetch_draft if some_update
98
+ rearchive
99
+ end
100
+
110
101
  rescued { delete_or_publish_draft }
111
102
  rescued { manage_remaps_table }
112
103
  end
@@ -126,10 +117,6 @@ class Eco::API::UseCases::GraphQL::Samples::Location
126
117
  input_response
127
118
  )
128
119
 
129
- # @todo: might need to set the name and the id by treeifying here.
130
- # Alternatively, add named arguments id and name.
131
- track_current_tree(results.structure)
132
-
133
120
  # early detection of errors
134
121
  self.error ||= page_errors?(
135
122
  input_response,
@@ -160,7 +147,11 @@ class Eco::API::UseCases::GraphQL::Samples::Location
160
147
  rearchive_commands = rearchive_comparer.stage_commands(:archive)
161
148
  return unless rearchive_commands.any?
162
149
 
163
- results.draft ||= create_draft(current_tree.id).draft
150
+ # create draft, just in case it wasn't already created
151
+ results.draft ||= create_draft(
152
+ results.structure_id,
153
+ &create_payload_without_structure_block
154
+ ).draft
164
155
 
165
156
  stage = :rearchive
166
157
 
@@ -172,8 +163,7 @@ class Eco::API::UseCases::GraphQL::Samples::Location
172
163
 
173
164
  sliced_batches(
174
165
  archive_input,
175
- desc: stage,
176
- track_tree_mode: :once,
166
+ desc: stage,
177
167
  &results_tracking_block(stage: stage)
178
168
  )
179
169
  rescue StandardError => err
@@ -182,6 +172,7 @@ class Eco::API::UseCases::GraphQL::Samples::Location
182
172
  end
183
173
  end
184
174
 
175
+ # @note this is the 2nd and last spot where we do require a current tree structure.
185
176
  def rearchive_comparer
186
177
  @rearchive_comparer ||= nodes_diff_class.new(
187
178
  as_nodes_json(current_tree), # hash_list(current_tree),
@@ -192,6 +183,19 @@ class Eco::API::UseCases::GraphQL::Samples::Location
192
183
  end
193
184
  alias_method :rearchive_diffs, :rearchive_comparer
194
185
 
186
+ def refetch_draft
187
+ return unless results.draft
188
+
189
+ results.draft = fetch_draft(
190
+ results.draft_id,
191
+ structure_id: results.structure_id
192
+ )
193
+
194
+ track_current_tree(results.structure)
195
+
196
+ results.draft
197
+ end
198
+
195
199
  def delete_or_publish_draft
196
200
  return unless results.draft_id
197
201
 
@@ -206,7 +210,10 @@ class Eco::API::UseCases::GraphQL::Samples::Location
206
210
  delete_draft(results.draft_id) if delete
207
211
  else
208
212
  results.final_response(
209
- publish_draft(results.draft_id)
213
+ publish_draft(
214
+ results.draft_id,
215
+ &publish_payload_without_structure_block
216
+ )
210
217
  )
211
218
  end
212
219
  end
@@ -18,8 +18,8 @@ class Eco::API::UseCases::GraphQL::Samples::Location
18
18
  end
19
19
 
20
20
  # Capture results
21
- def results
22
- @results ||= run_results_class.new
21
+ def results(target_tree = nil)
22
+ @results ||= run_results_class.new(target_tree)
23
23
  end
24
24
 
25
25
  # Errors tracking/logging.
@@ -69,6 +69,7 @@ class Eco::Data::Locations::NodeDiff::NodesDiff
69
69
  %i[unarchive? id? id_name? insert? move? archive?].each do |stage|
70
70
  puts "Stage #{stage}: #{diff.send(stage)}"
71
71
  end
72
+
72
73
  msg = "Node '#{id}' seems to be parent of itself"
73
74
  raise DataIntegrityError, msg
74
75
  end
data/lib/eco/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Eco
2
- VERSION = '3.2.3'.freeze
2
+ VERSION = '3.2.4'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eco-helpers
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.3
4
+ version: 3.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oscar Segura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-21 00:00:00.000000000 Z
11
+ date: 2025-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: byebug
@@ -263,7 +263,7 @@ dependencies:
263
263
  version: '1.3'
264
264
  - - ">="
265
265
  - !ruby/object:Gem::Version
266
- version: 1.3.2
266
+ version: 1.3.3
267
267
  type: :runtime
268
268
  prerelease: false
269
269
  version_requirements: !ruby/object:Gem::Requirement
@@ -273,7 +273,7 @@ dependencies:
273
273
  version: '1.3'
274
274
  - - ">="
275
275
  - !ruby/object:Gem::Version
276
- version: 1.3.2
276
+ version: 1.3.3
277
277
  - !ruby/object:Gem::Dependency
278
278
  name: ecoportal-api-v2
279
279
  requirement: !ruby/object:Gem::Requirement