eco-helpers 3.2.2 → 3.2.3

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +16 -1
  4. data/eco-helpers.gemspec +2 -2
  5. data/lib/eco/api/common/loaders/config/workflow/mailer.rb +1 -0
  6. data/lib/eco/api/common/people/person_entry.rb +1 -1
  7. data/lib/eco/api/common/people/person_parser.rb +1 -0
  8. data/lib/eco/api/organization/tag_tree.rb +11 -4
  9. data/lib/eco/api/usecases/default/locations/tagtree_upload_case.rb +20 -2
  10. data/lib/eco/api/usecases/graphql/helpers/location/base/tree_tracking.rb +14 -7
  11. data/lib/eco/api/usecases/graphql/helpers/location/base.rb +1 -1
  12. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/as_update.rb +0 -1
  13. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_archive.rb +0 -1
  14. data/lib/eco/api/usecases/graphql/helpers/location/command/end_points/optimizations.rb +64 -0
  15. data/lib/eco/api/usecases/graphql/helpers/location/command/end_points.rb +96 -0
  16. data/lib/eco/api/usecases/graphql/helpers/location/command/input_unit_response.rb +69 -0
  17. data/lib/eco/api/usecases/graphql/helpers/location/command/result.rb +11 -10
  18. data/lib/eco/api/usecases/graphql/helpers/location/command/results.rb +120 -63
  19. data/lib/eco/api/usecases/graphql/helpers/location/command.rb +26 -26
  20. data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +195 -37
  21. data/lib/eco/api/usecases/graphql/samples/location/command/results.rb +45 -13
  22. data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +0 -51
  23. data/lib/eco/api/usecases/graphql/samples/location/command/track_changed_ids.rb +6 -14
  24. data/lib/eco/api/usecases/graphql/samples/location/command.rb +1 -1
  25. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/inputable.rb +4 -1
  26. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff.rb +6 -0
  27. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/input.rb +1 -0
  28. data/lib/eco/api/usecases/lib/error_handling.rb +1 -1
  29. data/lib/eco/api/usecases/ooze_samples/helpers/creatable.rb +1 -0
  30. data/lib/eco/api/usecases/ooze_samples/helpers/rescuable.rb +1 -0
  31. data/lib/eco/data/hashes/array_diff.rb +14 -4
  32. data/lib/eco/data/locations/node_base/csv_convert.rb +9 -1
  33. data/lib/eco/data/locations/node_diff/nodes_diff.rb +6 -0
  34. data/lib/eco/version.rb +1 -1
  35. metadata +9 -7
  36. data/lib/eco/api/usecases/graphql/helpers/location/command/optimizations.rb +0 -84
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 415f7627b23706adc59420467cd7491f5990c267109a11f9128fa8dac908631c
4
- data.tar.gz: 8f04152b0e476c656c83fe752b091e83e042a322d0cdbb69e26bb8650c812f07
3
+ metadata.gz: b3323b0394649126c76ec4239eade6cca337049bd8f761f96aa7c66034717bd0
4
+ data.tar.gz: 3ddedb00b3b1e7674e014143e54e9cb46e52abb9482e9dfbc65dd4059fd0a70e
5
5
  SHA512:
6
- metadata.gz: 9cb01334ae6ae73d528f5841c568ee3f13305cabf5051d5ef2bee3775d9dac5d6618a16436f62fa28ff9c06ba628ca1c04897e528ba3a367db8a8805affc4f44
7
- data.tar.gz: 6e9fe1d5c9cbda796692302ad551702c446ee73a44bdd83cb3e04a59de0ef3474e878d071d6977cd68fc7f0d19256a334a2c014c62ec79b63c918ad5d6463c48
6
+ metadata.gz: 6b6126ec90b13ef1d2d985b384215a529b5818ff5d8139d96fe80d5c21c6a4a0b367cb9bb7bf8b92ff6ed61f3df525f35e2639b5160693cc0e2fd6109bfb02cb
7
+ data.tar.gz: d4b799b66943fda1cf7340f358520d1f30715e27866ba900615cedc5bab593200a53cb2ef72bb6ff04c8e8b4e7c4b1b6bd1b0240145240364f67f633fbb3fbd6
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 3.2.2
2
+ TargetRubyVersion: 3.2
3
3
  Exclude:
4
4
  - 'config/routes.rb'
5
5
  NewCops: enable
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.3.1] - 2025-06-xx
5
+ ## [3.2.4] - 2025-08-xx
6
6
 
7
7
  ### Added
8
8
 
@@ -10,6 +10,21 @@ All notable changes to this project will be documented in this file.
10
10
 
11
11
  ### Fixed
12
12
 
13
+ ## [3.2.3] - 2025-08-21
14
+
15
+ ### Added
16
+
17
+ - `Person#phone_number`
18
+
19
+ ### Changed
20
+
21
+ - upgraded dependencies
22
+ - `ecoportal-api` gem
23
+
24
+ ### Fixed
25
+
26
+ - Switched to use DRAFTs on RS updates
27
+
13
28
  ## [3.2.2] - 2025-06-11
14
29
 
15
30
  ### Added
data/eco-helpers.gemspec CHANGED
@@ -41,8 +41,8 @@ Gem::Specification.new do |spec|
41
41
  spec.add_dependency 'bcrypt_pbkdf', '~> 1.0'
42
42
  spec.add_dependency 'docx', '>= 0.8.0', '< 0.9'
43
43
  spec.add_dependency 'dotenv', '~> 3'
44
- spec.add_dependency 'ecoportal-api', '~> 0.10', '>= 0.10.11'
45
- spec.add_dependency 'ecoportal-api-graphql', '~> 1.3', '>= 1.3.1'
44
+ spec.add_dependency 'ecoportal-api', '~> 0.10', '>= 0.10.14'
45
+ spec.add_dependency 'ecoportal-api-graphql', '~> 1.3', '>= 1.3.2'
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'
@@ -75,6 +75,7 @@ class Eco::API::Common::Loaders::Workflow::Mailer < Eco::API::Common::Loaders::W
75
75
  host(io).downcase.start_with?('live.')
76
76
  end
77
77
 
78
+ # @note this initiates a connection to the API (undesired)
78
79
  def host(io)
79
80
  io.session.api.client.host
80
81
  end
@@ -9,7 +9,7 @@ module Eco
9
9
  BOOLEAN_ATTRS = %w[
10
10
  freemium accept_eula archived
11
11
  ].freeze
12
- MULTI_ATTRS = %w[
12
+ MULTI_ATTRS = %w[
13
13
  policy_group_ids filter_tags login_provider_ids starred_ids
14
14
  ].freeze
15
15
  NUMERIC_ATTRS = %w[
@@ -17,6 +17,7 @@ module Eco
17
17
 
18
18
  CORE_ATTRS = %w[
19
19
  id external_id email name
20
+ phone_number
20
21
  supervisor_id filter_tags
21
22
  archived
22
23
  contractor_organization_id
@@ -68,7 +68,13 @@ module Eco
68
68
  @path.push(self.id) unless top?
69
69
 
70
70
  @nodes = @raw_nodes.map.with_index do |cnode, idx|
71
- self.class.new(cnode, depth: depth + 1, path: @path.dup, parent: self, _weight: idx)
71
+ self.class.new(
72
+ cnode,
73
+ depth: depth + 1,
74
+ path: @path.dup,
75
+ parent: self,
76
+ _weight: idx
77
+ )
72
78
  end
73
79
 
74
80
  init_hashes
@@ -199,8 +205,9 @@ module Eco
199
205
  # @param max_depth [Boolean] up to what level `depth` nodes should be included.
200
206
  # @return [Array[Hash]] where `Hash` is a `node` (i.e. `{"tag" => TAG, "nodes": Array[Hash]}`)
201
207
  def as_json( # rubocop:disable Metrics/AbcSize
202
- include_children: true, include_archived: true,
203
- max_depth: total_depth,
208
+ include_children: true,
209
+ include_archived: true,
210
+ max_depth: total_depth,
204
211
  &block
205
212
  )
206
213
  max_depth ||= total_depth
@@ -230,7 +237,7 @@ module Eco
230
237
  depth + 1
231
238
  ]
232
239
  node_json = self.class::HEADER.zip(values).to_h
233
- node_json["nodes"] = children_json if include_children
240
+ node_json['nodes'] = children_json if include_children
234
241
  node_json = yield(node_json, self) if block_given?
235
242
  node_json
236
243
  end
@@ -1,7 +1,7 @@
1
1
  class Eco::API::UseCases::Default::Locations::TagtreeUpload < Eco::API::UseCases::GraphQL::Samples::Location::Command
2
2
  require_relative 'cli/tagtree_upload_cli'
3
3
 
4
- name "tagtree-upload"
4
+ name 'tagtree-upload'
5
5
  type :other
6
6
 
7
7
  COMMANDS_PER_PAGE = 45
@@ -11,10 +11,28 @@ class Eco::API::UseCases::Default::Locations::TagtreeUpload < Eco::API::UseCases
11
11
  false
12
12
  end
13
13
 
14
+ def compare_live_with_file?
15
+ true
16
+ end
17
+
18
+ # # With given the commands, it generates the input of the endpoint mutation.
19
+ # # @param commands [Array<Hash>]
20
+ # def input(commands, force_continue: force_continue?)
21
+ # {
22
+ # clientMutationId: '',
23
+ # id: target_structure_id,
24
+ # force: force_continue,
25
+ # commands: commands
26
+ # }
27
+ # end
28
+
14
29
  # We only define the `:insert` stage
15
30
  def inputs(force_continue: force_continue?)
16
31
  {}.tap do |stages|
17
- stages[:insert] = input(insert_commands, force_continue: force_continue)
32
+ stages[:insert] = input(
33
+ insert_commands,
34
+ force_continue: force_continue
35
+ )
18
36
  end.each do |stage, input|
19
37
  yield(input, stage) if block_given?
20
38
  end
@@ -29,16 +29,20 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
29
29
  # At any moment we want to know how the live tree is
30
30
  # @note it also does a backup
31
31
  # @return [Eco::API::Organization::TagTree] the latest tree (`current_tree`)
32
- def track_current_tree(tree)
32
+ def track_current_tree(tree) # rubocop:disable Metrics/AbcSize
33
+ return unless track_current_tree?
33
34
  return if simulate?
34
35
  return if tree.nil?
35
36
 
36
- latest_tree = tree if tree.is_a?(Eco::API::Organization::TagTree)
37
- if tree.respond_to?(:treeify) && track_current_tree?
37
+ tree_id = current_tree&.id
38
+ tree_name = current_tree&.name
39
+ tree_id = tree.id if tree.respond_to?(:id)
40
+ tree_name = tree.name if tree.respond_to?(:name)
41
+ latest_tree = tree if tree.is_a?(Eco::API::Organization::TagTree)
42
+
43
+ if tree.respond_to?(:treeify)
38
44
  latest_tree ||= Eco::API::Organization::TagTree.new(
39
- tree.treeify,
40
- id: tree.id,
41
- name: tree.name
45
+ tree.treeify
42
46
  )
43
47
  end
44
48
 
@@ -46,6 +50,9 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
46
50
  next unless latest_tree.is_a?(Eco::API::Organization::TagTree)
47
51
  next if latest_tree.empty?
48
52
 
53
+ latest_tree.id = tree_id if latest_tree.id.nil?
54
+ latest_tree.name = tree_name if latest_tree.name.nil?
55
+
49
56
  # a backup happens:
50
57
  self.current_tree = latest_tree
51
58
  end
@@ -53,7 +60,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
53
60
 
54
61
  # @param tree [Eco::API::Organization::TagTree, Hash, Array]
55
62
  # @return [Boolean] whether or not the backup was created
56
- def backup_tree(tree = current_tree || live_tree)
63
+ def backup_tree(tree = current_tree || live_tree) # rubocop:disable Naming/PredicateMethod
57
64
  return false if simulate?
58
65
  case tree
59
66
  when Eco::API::Organization::TagTree
@@ -22,7 +22,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
22
22
  log(:warn) { msg }
23
23
 
24
24
  # a backup happens:
25
- return nil unless (self.current_tree = session_live_tree)
25
+ return unless (self.current_tree = session_live_tree)
26
26
 
27
27
  @target_structure_id = current_tree.id
28
28
  end
@@ -18,7 +18,6 @@ class Eco::API::UseCases::GraphQL::Helpers::Location::Command::Diff
18
18
  class_ids = class_ids.compact.map {|val| val.split('|')}.flatten.uniq
19
19
  h['classificationIds'] = class_ids
20
20
  end
21
-
22
21
  if archive? || insert?
23
22
  # We assume archives do not have `move` nor update `id` or `name`
24
23
  h['nodeId'] = node_id || node_id_prev
@@ -2,7 +2,6 @@ class Eco::API::UseCases::GraphQL::Helpers::Location::Command::Diffs
2
2
  module Stages
3
3
  module DiffSortable
4
4
  module ForArchive
5
-
6
5
  private
7
6
 
8
7
  def only_first_ancestor_in_chains(raw_diffs)
@@ -0,0 +1,64 @@
1
+ module Eco::API::UseCases::GraphQL::Helpers::Location
2
+ module Command::EndPoints
3
+ module Optimizations
4
+ DEFAULT_COMMANDS_PER_PAGE = 45
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
+ # Prevents each request from timing out
33
+ def commands_per_page
34
+ if self.class.const_defined?(:COMMANDS_PER_PAGE)
35
+ self.class::COMMANDS_PER_PAGE
36
+ else
37
+ DEFAULT_COMMANDS_PER_PAGE
38
+ end
39
+ end
40
+
41
+ # Whether to stop or continue on command fail
42
+ def force_continue?
43
+ false
44
+ end
45
+
46
+ # Commands payload without querying Structure
47
+ # @note this servces the purpose of optimizing/speeding up the requests.
48
+ def commands_payload_without_structure_block
49
+ proc {
50
+ clientMutationId
51
+ ok
52
+ errors { # rubocop:disable Style/BlockDelimiters
53
+ details
54
+ fullMessages
55
+ messages
56
+ }
57
+ draft { # rubocop:disable Style/BlockDelimiters
58
+ ___Ecoportal__API__GraphQL__Fragment__LocationDraft
59
+ }
60
+ }
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,96 @@
1
+ module Eco::API::UseCases::GraphQL::Helpers::Location
2
+ module Command::EndPoints
3
+ include Eco::Language::AuxiliarLogger
4
+ include Eco::API::UseCases::GraphQL::Helpers::Location::Base
5
+
6
+ require_relative 'end_points/optimizations'
7
+
8
+ include Optimizations
9
+
10
+ def create_draft(
11
+ structure_id,
12
+ name: Time.now.iso8601,
13
+ notes: '',
14
+ include_archived_nodes: true
15
+ )
16
+ log(:info) { "Going to create draft (for #{structure_id})..." }
17
+
18
+ graphql.locationStructure.draft.create(
19
+ input: {
20
+ structureId: structure_id,
21
+ name: name,
22
+ notes: notes
23
+ },
24
+ includeArchivedNodes: include_archived_nodes
25
+ ).tap do |payload|
26
+ unless payload.error?
27
+ log(:info) { "Created draft '#{payload.draft&.id}' (for #{structure_id})" }
28
+ next
29
+ end
30
+
31
+ msg = "Could not create draft for #{structure_id}:\n"
32
+ msg << JSON.pretty_generate(payload.error_doc)
33
+
34
+ raise StandardError, msg
35
+ end
36
+ end
37
+
38
+ def delete_draft(draft_id)
39
+ graphql.locationStructure.draft.delete(
40
+ input: {
41
+ id: draft_id
42
+ }
43
+ ).tap do |payload|
44
+ unless payload.error?
45
+ log(:info) { "Deleted draft #{draft_id}" }
46
+ next
47
+ end
48
+
49
+ msg = "Could not delete draft #{draft_id}:\n"
50
+ msg << JSON.pretty_generate(payload.error_doc)
51
+
52
+ raise StandardError, msg
53
+ end
54
+ end
55
+
56
+ def publish_draft(
57
+ draft_id,
58
+ preview: false,
59
+ force: false,
60
+ include_archived_nodes: true
61
+ )
62
+ log(:info) { "Going to publish draft #{draft_id}..." }
63
+
64
+ graphql.locationStructure.draft.publish(
65
+ input: {
66
+ id: draft_id,
67
+ preview: preview,
68
+ force: force
69
+ },
70
+ includeArchivedNodes: include_archived_nodes
71
+ ).tap do |payload|
72
+ unless payload.error?
73
+ log(:info) { "Published draft #{draft_id}" }
74
+ next
75
+ end
76
+
77
+ msg = "Could not publish draft #{draft_id}:\n"
78
+ msg << JSON.pretty_generate(payload.error_doc)
79
+
80
+ raise StandardError, msg
81
+ end
82
+ end
83
+
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
+ end
95
+ end
96
+ end
@@ -0,0 +1,69 @@
1
+ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
2
+ class InputUnitResponse
3
+ attr_reader :input, :response
4
+
5
+ def initialize(input, response)
6
+ @input = input
7
+ @response = response
8
+ end
9
+
10
+ def draft
11
+ response&.draft
12
+ end
13
+
14
+ def structure
15
+ draft&.structure
16
+ end
17
+
18
+ def commands
19
+ input[:commands]
20
+ end
21
+
22
+ # brief summary of the error
23
+ def error_doc
24
+ if (err_doc = response&.error_doc)
25
+ return err_doc
26
+ end
27
+
28
+ conflictingIds if conflictingIds?
29
+ end
30
+
31
+ def error?
32
+ return false if response.nil?
33
+ return true if conflictingIds?
34
+
35
+ response.error? # draft might have previous, but we will stop anyway!
36
+ end
37
+ alias_method :errors?, :error?
38
+
39
+ def ok?
40
+ !errors?
41
+ end
42
+
43
+ def conflictingIds? # rubocop:disable Naming/MethodName
44
+ conflictingIds.any?
45
+ end
46
+
47
+ def conflictingIds # rubocop:disable Naming/MethodName
48
+ draft_conflicting_ids & target_ids
49
+ end
50
+
51
+ private
52
+
53
+ def draft_conflicting_ids
54
+ return [] unless draft
55
+
56
+ draft.conflictingIds || []
57
+ end
58
+
59
+ def target_ids
60
+ return [] unless commands.is_a?(Array)
61
+
62
+ commands.map do |cmd|
63
+ next cmd.target_ids if cmd.respond_to?(:target_ids)
64
+
65
+ cmd.values_at(:nodeId, :newId, :id)
66
+ end.flatten.compact.uniq
67
+ end
68
+ end
69
+ end
@@ -7,14 +7,11 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
7
7
  @result = result
8
8
  end
9
9
 
10
- def command
10
+ # @return [Symbol] the command type
11
+ def command_type
11
12
  input.keys.first
12
13
  end
13
14
 
14
- def command_input_data
15
- input[command]
16
- end
17
-
18
15
  def node_id
19
16
  command_input_data[:nodeId]
20
17
  end
@@ -24,7 +21,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
24
21
  end
25
22
 
26
23
  def applied?
27
- !pending?
24
+ !pending? && success?
28
25
  end
29
26
 
30
27
  def success?
@@ -47,20 +44,24 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
47
44
 
48
45
  feed = []
49
46
  feed.concat(error.validationErrors.map(&:message)) unless error.validationErrors.empty?
50
- feed << "Command: #{command_input_data.pretty_inspect}"
47
+ feed << "Command: #{JSON.pretty_generate(command_input_data)}"
51
48
 
52
49
  msg << " * #{feed.join("\n * ")}"
53
50
  msg.join("\n")
54
51
  end
55
52
 
56
- def command_result
53
+ def command_result_data
57
54
  result&.command
58
55
  end
59
56
 
57
+ def command_input_data
58
+ input[command]
59
+ end
60
+
60
61
  def command_id
61
- return unless result
62
+ return unless command_result_data
62
63
 
63
- command_result['id']
64
+ command_result_data['id']
64
65
  end
65
66
 
66
67
  def as_json