eco-helpers 2.6.3 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +95 -0
  4. data/CHANGELOG.md +135 -2
  5. data/Rakefile +13 -7
  6. data/eco-helpers.gemspec +3 -3
  7. data/lib/eco/api/common/loaders/base.rb +2 -2
  8. data/lib/eco/api/common/loaders/case_base.rb +1 -1
  9. data/lib/eco/api/common/loaders/config/workflow/mailer.rb +5 -5
  10. data/lib/eco/api/common/loaders/error_handler.rb +8 -5
  11. data/lib/eco/api/common/loaders/parser.rb +44 -22
  12. data/lib/eco/api/common/loaders/policy.rb +6 -4
  13. data/lib/eco/api/common/loaders/use_case.rb +13 -7
  14. data/lib/eco/api/common/people/base_parser.rb +0 -2
  15. data/lib/eco/api/common/people/default_parsers/boolean_parser.rb +0 -1
  16. data/lib/eco/api/common/people/default_parsers/csv_parser.rb +1 -1
  17. data/lib/eco/api/common/people/default_parsers/date_parser.rb +64 -12
  18. data/lib/eco/api/common/people/default_parsers/freemium_parser.rb +0 -1
  19. data/lib/eco/api/common/people/default_parsers/login_providers_parser.rb +13 -5
  20. data/lib/eco/api/common/people/default_parsers/multi_parser.rb +0 -1
  21. data/lib/eco/api/common/people/default_parsers/numeric_parser.rb +18 -5
  22. data/lib/eco/api/common/people/default_parsers/policy_groups_parser.rb +8 -8
  23. data/lib/eco/api/common/people/default_parsers/select_parser.rb +50 -26
  24. data/lib/eco/api/common/people/default_parsers/send_invites_parser.rb +6 -6
  25. data/lib/eco/api/common/people/default_parsers/xls_parser.rb +9 -12
  26. data/lib/eco/api/common/people/default_parsers.rb +1 -12
  27. data/lib/eco/api/common/people/entries.rb +13 -13
  28. data/lib/eco/api/common/people/entry_factory.rb +76 -45
  29. data/lib/eco/api/common/people/person_attribute_parser.rb +8 -12
  30. data/lib/eco/api/common/people/person_entry.rb +86 -75
  31. data/lib/eco/api/common/people/person_entry_attribute_mapper.rb +60 -44
  32. data/lib/eco/api/common/people/person_factory.rb +30 -22
  33. data/lib/eco/api/common/people/person_modifier.rb +11 -13
  34. data/lib/eco/api/common/people/person_parser.rb +101 -39
  35. data/lib/eco/api/common/people/supervisor_helpers.rb +25 -26
  36. data/lib/eco/api/common/session/base_session.rb +9 -9
  37. data/lib/eco/api/common/session/environment.rb +7 -5
  38. data/lib/eco/api/common/session/sftp.rb +59 -32
  39. data/lib/eco/api/common/version_patches/ecoportal_api/external_person.rb +10 -6
  40. data/lib/eco/api/common/version_patches/exception.rb +11 -13
  41. data/lib/eco/api/error.rb +32 -20
  42. data/lib/eco/api/microcases/set_supervisor.rb +0 -3
  43. data/lib/eco/api/organization/node_classifications.rb +82 -0
  44. data/lib/eco/api/organization/policy_groups.rb +4 -6
  45. data/lib/eco/api/organization/tag_tree.rb +169 -93
  46. data/lib/eco/api/organization.rb +1 -0
  47. data/lib/eco/api/session/batch/job.rb +1 -1
  48. data/lib/eco/api/session/config/tagtree.rb +41 -23
  49. data/lib/eco/api/session/config/workflow.rb +113 -88
  50. data/lib/eco/api/session/config.rb +6 -0
  51. data/lib/eco/api/session.rb +51 -29
  52. data/lib/eco/api/usecases/base_io.rb +28 -25
  53. data/lib/eco/api/usecases/default/locations/cli/tagtree_extract_cli.rb +7 -2
  54. data/lib/eco/api/usecases/default/locations/cli/tagtree_upload_cli.rb +21 -0
  55. data/lib/eco/api/usecases/default/locations/csv_to_tree_case.rb +3 -3
  56. data/lib/eco/api/usecases/default/locations/tagtree_extract_case.rb +54 -23
  57. data/lib/eco/api/usecases/default/locations/tagtree_upload_case.rb +87 -0
  58. data/lib/eco/api/usecases/default/locations.rb +1 -0
  59. data/lib/eco/api/usecases/default/people/analyse_people_case.rb +60 -56
  60. data/lib/eco/api/usecases/default/people/change_email_case.rb +8 -9
  61. data/lib/eco/api/usecases/default/people/clean_unknown_tags_case.rb +13 -11
  62. data/lib/eco/api/usecases/default/people/clear_abilities_case.rb +2 -2
  63. data/lib/eco/api/usecases/default/people/org_data_convert_case.rb +25 -27
  64. data/lib/eco/api/usecases/default/people/refresh_case.rb +2 -2
  65. data/lib/eco/api/usecases/default/people/reinvite_trans_case.rb +1 -1
  66. data/lib/eco/api/usecases/default/people/reinvite_trans_cli.rb +0 -1
  67. data/lib/eco/api/usecases/default/people/restore_db_case.rb +39 -34
  68. data/lib/eco/api/usecases/default/people/set_default_tag_case.rb +19 -15
  69. data/lib/eco/api/usecases/default/people/supers_cyclic_identify_case.rb +16 -12
  70. data/lib/eco/api/usecases/default_cases/hris_case.rb +17 -15
  71. data/lib/eco/api/usecases/default_cases/samples/sftp_case.rb +30 -16
  72. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +0 -2
  73. data/lib/eco/api/usecases/graphql/base.rb +5 -3
  74. data/lib/eco/api/usecases/graphql/helpers/base/case_env.rb +4 -1
  75. data/lib/eco/api/usecases/graphql/helpers/base/graphql_env.rb +14 -0
  76. data/lib/eco/api/usecases/graphql/helpers/base.rb +5 -4
  77. data/lib/eco/api/usecases/graphql/helpers/location/base/classifications_parser.rb +60 -0
  78. data/lib/eco/api/usecases/graphql/helpers/location/base/tree_tracking.rb +72 -0
  79. data/lib/eco/api/usecases/graphql/helpers/location/base.rb +25 -59
  80. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/as_update.rb +59 -0
  81. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/compare.rb +49 -0
  82. data/lib/eco/api/usecases/graphql/helpers/location/command/diff.rb +11 -0
  83. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/commandable.rb +46 -0
  84. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_archive.rb +23 -0
  85. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_unarchive.rb +65 -0
  86. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable.rb +49 -0
  87. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/sortable/relation_safe_sort.rb +119 -0
  88. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/sortable.rb +59 -0
  89. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages.rb +82 -0
  90. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs.rb +20 -0
  91. data/lib/eco/api/usecases/graphql/helpers/location/command/optimizations.rb +84 -0
  92. data/lib/eco/api/usecases/graphql/helpers/location/command/result.rb +4 -4
  93. data/lib/eco/api/usecases/graphql/helpers/location/command/results.rb +24 -12
  94. data/lib/eco/api/usecases/graphql/helpers/location/command.rb +21 -24
  95. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap/tags_map.rb +1 -1
  96. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap/tags_set.rb +10 -11
  97. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap.rb +8 -9
  98. data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +41 -12
  99. data/lib/eco/api/usecases/graphql/samples/location/command/results.rb +11 -80
  100. data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +89 -0
  101. data/lib/eco/api/usecases/graphql/samples/location/command/service.rb +6 -0
  102. data/lib/eco/api/usecases/graphql/samples/location/command/track_changed_ids.rb +89 -0
  103. data/lib/eco/api/usecases/graphql/samples/location/command.rb +3 -0
  104. data/lib/eco/api/usecases/graphql/samples/location/service/base.rb +9 -0
  105. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/heading.rb +18 -0
  106. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/inputable.rb +53 -0
  107. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing/classifications.rb +34 -0
  108. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing/helpers.rb +28 -0
  109. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing.rb +46 -0
  110. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible.rb +38 -0
  111. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff.rb +105 -0
  112. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/discarded.rb +16 -0
  113. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/input.rb +15 -0
  114. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/node_attr_maps.rb +22 -0
  115. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/parser.rb +45 -0
  116. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter.rb +36 -0
  117. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/output.rb +56 -0
  118. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list.rb +41 -0
  119. data/lib/eco/api/usecases/graphql/samples/location/service.rb +8 -0
  120. data/lib/eco/api/usecases/graphql/samples/location.rb +1 -0
  121. data/lib/eco/api/usecases/graphql/utils/sftp.rb +96 -36
  122. data/lib/eco/api/usecases/ooze_cases/export_register_case.rb +8 -6
  123. data/lib/eco/api/usecases/ooze_samples/helpers/creatable.rb +4 -3
  124. data/lib/eco/api/usecases/ooze_samples/helpers/exportable_ooze.rb +39 -25
  125. data/lib/eco/api/usecases/ooze_samples/helpers/exportable_register.rb +13 -15
  126. data/lib/eco/api/usecases/ooze_samples/helpers/filters.rb +50 -21
  127. data/lib/eco/api/usecases/ooze_samples/helpers/ooze_handlers.rb +21 -11
  128. data/lib/eco/api/usecases/ooze_samples/helpers/rescuable.rb +2 -0
  129. data/lib/eco/api/usecases/ooze_samples/helpers/shortcuts.rb +49 -43
  130. data/lib/eco/api/usecases/ooze_samples/ooze_base_case.rb +17 -19
  131. data/lib/eco/api/usecases/ooze_samples/register_export_case.rb +48 -43
  132. data/lib/eco/api/usecases/ooze_samples/register_update_case.rb +34 -34
  133. data/lib/eco/api/usecases/ooze_samples/target_oozes_update_case.rb +8 -10
  134. data/lib/eco/api/usecases.rb +0 -1
  135. data/lib/eco/cli/config/use_cases.rb +31 -29
  136. data/lib/eco/cli_default/input_filters.rb +0 -5
  137. data/lib/eco/cli_default/people_filters.rb +4 -4
  138. data/lib/eco/cli_default/workflow.rb +13 -14
  139. data/lib/eco/csv/table.rb +34 -25
  140. data/lib/eco/data/hashes/array_diff.rb +24 -35
  141. data/lib/eco/data/hashes/diff_result/meta.rb +131 -0
  142. data/lib/eco/data/hashes/diff_result.rb +65 -57
  143. data/lib/eco/data/hashes/sanke_camel_indifferent_access.rb +278 -0
  144. data/lib/eco/data/hashes.rb +1 -1
  145. data/lib/eco/data/locations/convert.rb +1 -1
  146. data/lib/eco/data/locations/node_base/csv_convert.rb +19 -9
  147. data/lib/eco/data/locations/node_base/parsing.rb +4 -2
  148. data/lib/eco/data/locations/node_base/treeify.rb +149 -132
  149. data/lib/eco/data/locations/node_base.rb +15 -4
  150. data/lib/eco/data/locations/node_diff/accessors.rb +13 -5
  151. data/lib/eco/data/locations/node_diff/nodes_diff/clustered_treeify.rb +101 -0
  152. data/lib/eco/data/locations/node_diff/nodes_diff/diffs_tree.rb +99 -0
  153. data/lib/eco/data/locations/node_diff/{selectors.rb → nodes_diff/selectors.rb} +1 -1
  154. data/lib/eco/data/locations/node_diff/nodes_diff.rb +50 -35
  155. data/lib/eco/data/locations/node_diff.rb +45 -17
  156. data/lib/eco/data/locations/node_level/parsing.rb +15 -21
  157. data/lib/eco/data/locations/node_level.rb +66 -22
  158. data/lib/eco/data/locations/node_plain/parsing.rb +1 -1
  159. data/lib/eco/data/locations/node_plain.rb +60 -7
  160. data/lib/eco/data/strings/camel_case.rb +35 -0
  161. data/lib/eco/data/strings/snake_case.rb +18 -0
  162. data/lib/eco/data/strings.rb +8 -0
  163. data/lib/eco/data.rb +1 -0
  164. data/lib/eco/language/methods/call_detector.rb +11 -0
  165. data/lib/eco/language/methods/dsl_able.rb +7 -1
  166. data/lib/eco/language/methods.rb +2 -1
  167. data/lib/eco/language/models/collection.rb +23 -25
  168. data/lib/eco/language/models/parser_serializer.rb +24 -5
  169. data/lib/eco/version.rb +1 -1
  170. data/lib/eco-helpers.rb +0 -1
  171. metadata +54 -9
  172. data/lib/eco/data/hashes/diff_meta.rb +0 -52
@@ -0,0 +1,82 @@
1
+ # @note The order in which updates should happen:
2
+ # (1) UNARCHIVE (top-bottom) should go FIRST, to ensure consistency througout all the stages sequence
3
+ # * Currently: CANNOT UPDATE (or move) ARCHIVED NODES
4
+ # * It can unarchive children with same archivedToken
5
+ # * Changing order to happen after MOVE does NOT prevent this to happen.
6
+ # * And archived children nodes cannot be moved somewhere else anyway either.
7
+ # * Within UNARCHIVE, the preferable order would be ALL nodes from leafs to root
8
+ # * to the purpose of preventing `already unarchived node` errors
9
+ # * because, from top to bottom, the `unarchivedToken` could have already unarchived children.
10
+ # * This still does not prevent the unintended consequence of automatic unarchive of children.
11
+ # * HOWEVER, the error _you can't unarchive a child of an archived node_ suggest that
12
+ # within UNARCHIVE, the order MUST BE from top-to-bottom, and MOREOVER
13
+ # * Among nodes with the same `archiveToken` only the topest one should be unarchived (the others follow),
14
+ # which prevents the errors `already unarchived node` and `can't unarchive child of archived node`
15
+ # * Children with same `archiveToken` that are not to be unarchived, should be rearchived
16
+ # * IMPORTANT: this could require to track all the way up to the first archived ancestor,
17
+ # path from leave-to archive-root, and then for unarchive from top to bottom only those
18
+ # with different archiveToken.
19
+ # (1.b) IMPORTANT: automatically unarchived nodes (archivedToken) that should remain archived
20
+ # should be RE-ARCHIVEd as part of the update (in the ARCHIVE stage)
21
+ # (2) UPDATE ID (--) needs to happen before
22
+ # * This allows other commands to refer to nodes and their parents with current ids.
23
+ # * If INSERT happened before UPDATE ID, they would need to refer to the previous value of `parentId`,
24
+ # but inserts don't have previous source in NodeDiff (they are new) and only a working-around (i.e. general lookup)
25
+ # would allow to obtain this value (not really neat)
26
+ # * Technically, the internal ORDER does NOT MATTER, but if something has to fail, let's make it fail
27
+ # big: so let's do top-to-bottom.
28
+ # (2.b) UPDATE NAME can happen anywhere, so let's keep tree consistent and do it as soon as possible.
29
+ # (3) INSERT (top-bottom) has priority over MOVE, as there may be existing nodes being moved to or inserted to.
30
+ # * And should happen AFTER the UNARCHIVE, because we could be inserting to an unarchived node
31
+ # and that would make our inserted node an archived one as well.
32
+ # * The internal ORDER MATTERS and it's from top to bottom (root to leaves) to prevent unknown parentId error.
33
+ # (4) MOVE (--) should happen before ARCHIVE and after UNARCHIVE
34
+ # * This prevents active nodes to be archived from their previous parent node, now being archived.
35
+ # * It also prevents the error of moving to an archived node, or not being able to move an archived node.
36
+ # * If it happened before UNARCHIVE, we would be either move ARCHIVED nodes to be unarchived,
37
+ # or moving other nodes to them before they are unarchived
38
+ # * Technically, for MOVE, the internal ORDER does NOT MATTER. It would matter for new/inserted nodes,
39
+ # but we do not move inserted nodes (we add them directly to the parent).
40
+ # * However, to build the remap tags table, it does matter: from bottom to top. Children should be
41
+ # retagged first because retagging first parents changes the path of their children retags.
42
+ # (5) ARCHIVE (bottom-top) should happen always LAST, after nodes have been MOVED.
43
+ # • The order of archiving should probably be:
44
+ # (a) although firstly thought go from leafs to root to prevent the `archiveToken`
45
+ # it should go from top-to-bottom, so precisely the `archiveToken` is generated and history well sorted.
46
+ # (b) to preventing `already archived node` errors, we should only include the parent
47
+ # (6) RE-ARCHIVE (bottom-top) see (1.b) all those that were automatically unarchived but should remain archived.
48
+ # • The neat solution is to recalculate changes with the live_tree and the final desired result,
49
+ # but perhaps only target those to be re-archived?
50
+ class Eco::API::UseCases::GraphQL::Helpers::Location::Command::Diffs
51
+ module Stages
52
+ STAGES = %i[unarchive id_name insert move archive].freeze
53
+
54
+ def stages
55
+ STAGES
56
+ end
57
+
58
+ def stage_idx(stage)
59
+ stages.index(stage)
60
+ end
61
+
62
+ def id_update_stage_idx
63
+ @id_update_stage_idx ||= stage_idx(:id) || stage_idx(:id_name)
64
+ end
65
+
66
+ def move_stage_idx
67
+ @move_stage_idx ||= stage_idx(:move)
68
+ end
69
+
70
+ def after_id_update?(stage)
71
+ (idx = stage_idx(stage)) && idx > id_update_stage_idx
72
+ end
73
+
74
+ def during_or_after_move?(stage)
75
+ (idx = stage_idx(stage)) && idx >= move_stage_idx
76
+ end
77
+ end
78
+ end
79
+
80
+ require_relative 'stages/sortable'
81
+ require_relative 'stages/diff_sortable'
82
+ require_relative 'stages/commandable'
@@ -0,0 +1,20 @@
1
+ # The order in which updates should happen:
2
+ # @note several challenges were found during the design of this class:
3
+ # This translated into dividing the command sequence into different `STAGES`.
4
+ module Eco::API::UseCases::GraphQL::Helpers::Location
5
+ class Command::Diffs < Eco::Data::Locations::NodeDiff::NodesDiff
6
+ class_resolver :diff_result_class, Command::Diff
7
+
8
+ require_relative 'diffs/stages'
9
+
10
+ include Stages::Commandable
11
+
12
+ def commands
13
+ self.class.stages.each_with_object([]) do |stage, out|
14
+ comms = stage_commands(stage)
15
+ out.concat(comms)
16
+ yield(comms, stage) if block_given?
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,84 @@
1
+ module Eco::API::UseCases::GraphQL::Helpers::Location
2
+ module Command::Optimizations
3
+ DEFAULT_COMMANDS_PER_PAGE = 45
4
+ DEFAULT_FORCE_CONTINUE = false
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
+ def default_tree_tracking_mode
11
+ :per_request
12
+ end
13
+
14
+ # Helper to identify the commands payload block
15
+ # @note
16
+ # 1. Gives flexibility on at what time the structure should be retrieved
17
+ # 2. This increases performacne, as we don't retrieve the full tree on
18
+ # each request unless necessary/specified
19
+ # 3. `nil` falls the block back to the `ecoportal-api-graphql` gem, which
20
+ # has a default block that retrieves the structure
21
+ # @return [Proc, NilClass] the payload block
22
+ def scope_commands_block(idx, total, track_tree_mode: default_tree_tracking_mode)
23
+ case track_tree_mode
24
+ when :per_request
25
+ nil
26
+ when :once
27
+ commands_payload_without_structure_block
28
+ when :per_batch
29
+ return nil if idx == total
30
+ commands_payload_without_structure_block
31
+ end
32
+ end
33
+
34
+ # Prevents each request from timing out
35
+ def commands_per_page
36
+ if self.class.const_defined?(:COMMANDS_PER_PAGE)
37
+ self.class::COMMANDS_PER_PAGE
38
+ else
39
+ DEFAULT_COMMANDS_PER_PAGE
40
+ end
41
+ end
42
+
43
+ # Whether to stop or continue on command fail
44
+ def force_continue?
45
+ if self.class.const_defined?(:FORCE_CONTINUE)
46
+ self.class::FORCE_CONTINUE
47
+ else
48
+ DEFAULT_FORCE_CONTINUE
49
+ end
50
+ end
51
+
52
+ # Commands payload without querying Structure
53
+ # @note this servces the purpose of optimizing/speeding up the requests.
54
+ def commands_payload_without_structure_block
55
+ proc {
56
+ clientMutationId
57
+ error { # rubocop:disable Style/BlockDelimiters
58
+ message
59
+ conflictingIds
60
+ validationErrors { # rubocop:disable Style/BlockDelimiters
61
+ error
62
+ message
63
+ }
64
+ }
65
+ results { # rubocop:disable Style/BlockDelimiters
66
+ command { # rubocop:disable Style/BlockDelimiters
67
+ id
68
+ state
69
+ __typename
70
+ }
71
+ ok
72
+ error { # rubocop:disable Style/BlockDelimiters
73
+ conflictingIds
74
+ message
75
+ validationErrors { # rubocop:disable Style/BlockDelimiters
76
+ error
77
+ message
78
+ }
79
+ }
80
+ }
81
+ }
82
+ end
83
+ end
84
+ end
@@ -42,11 +42,11 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
42
42
  def error_msg
43
43
  return nil unless error?
44
44
  msg = ''
45
- msg << "(#{command} '#{node_id}') #{error.message}\n" || ''
45
+ msg << "(#{command} '#{node_id}') #{error.message}\n"
46
46
  return msg if error.validationErrors.empty?
47
- msg << " • " + error.validationErrors.map do |err|
48
- err.message
49
- end.join("\n • ")
47
+
48
+ msg << " • "
49
+ msg << error.validationErrors.map(&:message).join("\n • ")
50
50
  msg
51
51
  end
52
52
 
@@ -8,12 +8,10 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
8
8
  end
9
9
 
10
10
  def stats
11
- msg = ''
12
- first_err = force? ? '' : "(stopped on node: '#{first_errored&.node_id}' - idx: #{first_errored_idx})"
13
- msg << " • Errored: #{errored.count} #{first_err}\n" if errored?
14
- last_okay = force? ? '' : "(last node done: '#{last_applied&.node_id}' - idx: #{last_applied_idx})"
15
- msg << " • Applied: #{errored.count} #{last_okay}\n" if some_applied?
16
- msg << " • Pending: #{pending.count}\n" if some_pending?
11
+ msg = ''
12
+ msg << " Errored: #{errored.count} #{first_err_str}\n" if errored?
13
+ msg << " • Applied: #{errored.count} #{last_okay_str}\n" if some_applied?
14
+ msg << " Pending: #{pending.count}\n" if some_pending?
17
15
  msg
18
16
  end
19
17
 
@@ -38,7 +36,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
38
36
  end
39
37
 
40
38
  def success?
41
- !error? && results.all? {|r| r.success?}
39
+ !error? && results.all?(&:success?)
42
40
  end
43
41
 
44
42
  def results
@@ -64,7 +62,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
64
62
  end
65
63
 
66
64
  def errored
67
- @errored ||= results.select {|r| r.error?}
65
+ @errored ||= results.select(&:error?)
68
66
  end
69
67
 
70
68
  def errored?
@@ -80,15 +78,15 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
80
78
  end
81
79
 
82
80
  def applied
83
- @applied ||= results.select {|r| r.applied?}
81
+ @applied ||= results.select(&:applied?)
84
82
  end
85
83
 
86
84
  def applied?
87
- results.all? {|r| r.applied?}
85
+ results.all?(&:applied?)
88
86
  end
89
87
 
90
88
  def some_applied?
91
- applied.count > 0
89
+ applied.count.positive?
92
90
  end
93
91
 
94
92
  def last_applied
@@ -100,7 +98,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
100
98
  end
101
99
 
102
100
  def pending
103
- @pending ||= results.select {|r| r.pending?}
101
+ @pending ||= results.select(&:pending?)
104
102
  end
105
103
 
106
104
  def some_pending?
@@ -122,5 +120,19 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
122
120
  def response_results
123
121
  response&.results || []
124
122
  end
123
+
124
+ # First error is relevant only if request was NOT forced to continue.
125
+ def first_err_str
126
+ return '' if force?
127
+ pre_str = 'stopped on node'
128
+ pre_str = 'first error' unless force?
129
+ "(#{pre_str}: '#{first_errored&.node_id}' - idx: #{first_errored_idx})"
130
+ end
131
+
132
+ # Last successfully applied is relevant only if request was NOT forced to continue.
133
+ def last_okay_str
134
+ return '' if force?
135
+ "(last node done: '#{last_applied&.node_id}' - idx: #{last_applied_idx})"
136
+ end
125
137
  end
126
138
  end
@@ -3,25 +3,12 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
3
3
  include Eco::Language::AuxiliarLogger
4
4
  include Eco::API::UseCases::GraphQL::Helpers::Location::Base
5
5
 
6
- DEFAULT_COMMANDS_PER_PAGE = 45
7
- DEFAULT_FORCE_CONTINUE = false
6
+ require_relative 'command/optimizations'
7
+ include Eco::API::UseCases::GraphQL::Helpers::Location::Command::Optimizations
8
8
 
9
- # Prevents each request from timing out
10
- def commands_per_page
11
- if self.class.const_defined?(:COMMANDS_PER_PAGE)
12
- self.class::COMMANDS_PER_PAGE
13
- else
14
- DEFAULT_COMMANDS_PER_PAGE
15
- end
16
- end
17
-
18
- # Whether to stop or continue on command fail
19
- def force_continue?
20
- if self.class.const_defined?(:FORCE_CONTINUE)
21
- self.class::FORCE_CONTINUE
22
- else
23
- DEFAULT_FORCE_CONTINUE
24
- end
9
+ # Actual GraphQL request
10
+ def apply_commands(input, &block)
11
+ graphql.locationStructure.applyCommands(input: input, &block)
25
12
  end
26
13
 
27
14
  # With given the commands, it generates the input of the endpoint mutation.
@@ -29,14 +16,20 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
29
16
  def input(commands, force_continue: force_continue?)
30
17
  {
31
18
  clientMutationId: "",
32
- id: target_structure_id,
33
- force: force_continue,
34
- commands: commands
19
+ id: target_structure_id,
20
+ force: force_continue,
21
+ commands: commands
35
22
  }
36
23
  end
37
24
 
25
+ # @param track_tree_mode [Symbol] when should updates happen
38
26
  # @return see #with_sliced_input
39
- def sliced_batches(batch_input, size: commands_per_page, desc: :input, logging: true)
27
+ def sliced_batches(
28
+ batch_input,
29
+ size: commands_per_page,
30
+ desc: :input,
31
+ track_tree_mode: default_tree_tracking_mode
32
+ )
40
33
  dry_run_msg = simulate? ? '(dry-run) ' : ''
41
34
 
42
35
  if batch_input[:commands].empty?
@@ -60,7 +53,8 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
60
53
  if simulate?
61
54
  log(:info) { sliced_input.pretty_inspect } if page < 3
62
55
  else
63
- response = graphql.locationStructure.applyCommands(input: sliced_input)
56
+ curr_block = scope_commands_block(page, pages, track_tree_mode: track_tree_mode)
57
+ response = apply_commands(sliced_input, &curr_block)
64
58
  backup(response, type: "tree_update_#{desc}_response_#{page}_of_#{pages}")
65
59
  end
66
60
 
@@ -75,7 +69,8 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
75
69
  comms = input_data[:commands]
76
70
  total = comms.count
77
71
  pages = (total.to_f / size).ceil.to_i
78
- page = 1; out = []
72
+ page = 1
73
+ out = []
79
74
  comms.each_slice(size) do |comms_slice|
80
75
  sliced_input = input_data.slice(:clientMutationId, :id).merge(commands: comms_slice)
81
76
  yield(sliced_input, page, pages, comms_slice.count, total).tap do |response|
@@ -90,3 +85,5 @@ end
90
85
 
91
86
  require_relative 'command/result'
92
87
  require_relative 'command/results'
88
+ require_relative 'command/diff'
89
+ require_relative 'command/diffs'
@@ -13,7 +13,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
13
13
  end
14
14
 
15
15
  # @note to create a stable sort we assume `self` is a sorted element
16
- def <=>(other)
16
+ def <=>(other) # rubocop:disable Metrics/AbcSize
17
17
  return -1 if maps? && !other.maps?
18
18
  return 1 if !maps? && other.maps?
19
19
  return -1 if rename? && other.move?
@@ -4,15 +4,16 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
4
4
  class << self
5
5
  def attr_compare(*attrs)
6
6
  attrs.each do |attr|
7
- meth = "#{attr}".to_sym
7
+ meth = "#{attr}".to_sym # rubocop:disable Style/RedundantInterpolation
8
8
  define_method meth do |value|
9
9
  set.send(meth, to_set(value))
10
10
  end
11
11
  end
12
12
  end
13
+
13
14
  def attr_operate(*attrs)
14
15
  attrs.each do |attr|
15
- meth = "#{attr}".to_sym
16
+ meth = "#{attr}".to_sym # rubocop:disable Style/RedundantInterpolation
16
17
  define_method meth do |value|
17
18
  self.class.new(set.send(meth, to_set(value)))
18
19
  end
@@ -21,12 +22,12 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
21
22
  end
22
23
 
23
24
  COMPARE_METHODS = %i[< <= == > >=].freeze
24
- OPERATE_METHIDS = %i[& + - ^].freeze
25
+ OPERATE_METHODS = %i[& + - ^].freeze
25
26
 
26
- attr_compare *COMPARE_METHODS
27
- attr_operate *OPERATE_METHIDS
27
+ attr_compare(*COMPARE_METHODS)
28
+ attr_operate(*OPERATE_METHODS)
28
29
 
29
- attr_reader :tags, :set
30
+ attr_reader :set
30
31
 
31
32
  def initialize(tags)
32
33
  @ini_tags = value_to_a(tags)
@@ -48,7 +49,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
48
49
  def to_s
49
50
  tags.join('|')
50
51
  end
51
-
52
+
52
53
  def empty?
53
54
  set.empty?
54
55
  end
@@ -60,7 +61,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
60
61
  end
61
62
 
62
63
  def include_any?(value)
63
- value.to_a.any? {|tag| self.include?(tag)}
64
+ value.to_a.any? {|tag| include?(tag)}
64
65
  end
65
66
 
66
67
  def subset_of?(value)
@@ -73,9 +74,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
73
74
 
74
75
  protected
75
76
 
76
- def ini_tags
77
- @ini_tags
78
- end
77
+ attr_reader :ini_tags
79
78
 
80
79
  private
81
80
 
@@ -5,9 +5,14 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
5
5
  correct_pair && pair.length == 2
6
6
  end
7
7
 
8
- attr_reader :src_maps
8
+ include Enumerable
9
+
9
10
  attr_reader :from, :to
10
11
 
12
+ def src_maps
13
+ @src_maps ||= []
14
+ end
15
+
11
16
  def each(&block)
12
17
  return to_enum(:each) unless block
13
18
  src_maps.each(&block)
@@ -15,7 +20,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
15
20
 
16
21
  def to_csv(filename)
17
22
  CSV.open(filename, "w") do |fd|
18
- fd << ["src_tags", "dst_tags"]
23
+ fd << %w[src_tags dst_tags]
19
24
  each do |tags_map|
20
25
  fd << tags_map.to_csv_row
21
26
  end
@@ -26,8 +31,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
26
31
  "".tap do |str|
27
32
  each.map do |tags_map|
28
33
  from, to = tags_map.to_csv_row
29
- str << " • from: #{from}\n"
30
- str << " • to: #{to}\n"
34
+ str << " • '#{from}' => '#{to}'\n"
31
35
  end
32
36
  end
33
37
  end
@@ -71,16 +75,11 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
71
75
 
72
76
  private
73
77
 
74
- attr_reader :src_maps
75
78
  attr_reader :from_key, :to_key
76
79
 
77
80
  def new_src(tags_map)
78
81
  src_maps << tags_map
79
82
  end
80
-
81
- def src_maps
82
- @src_maps ||= []
83
- end
84
83
  end
85
84
  end
86
85
 
@@ -1,6 +1,7 @@
1
1
  class Eco::API::UseCases::GraphQL::Samples::Location
2
2
  module Command::DSL
3
3
  include Eco::API::UseCases::GraphQL::Helpers::Location::Command
4
+ include Eco::API::UseCases::GraphQL::Samples::Location::Command::TrackChangedIds
4
5
  include Eco::API::UseCases::GraphQL::Samples::Location::Command::Results
5
6
 
6
7
  # @example of implementation:
@@ -15,40 +16,68 @@ class Eco::API::UseCases::GraphQL::Samples::Location
15
16
  # end
16
17
  # end
17
18
  # end
18
- def inputs(*args, force_continue: force_continue?, **kargs, &block)
19
+ def inputs(*_args, force_continue: force_continue?, **_kargs, &_block) # rubocop:disable Lint/UnusedMethodArgument
19
20
  msg = "You should implement this method in your child class.\n"
20
21
  msg << "Which should yield the input Hash and the stage or descriptor."
21
22
  raise Eco::API::UseCases::GraphQL::Base::NotImplementedMethod, msg
22
23
  end
23
24
 
24
25
  # Main processor
25
- def process
26
- begin
27
- super if defined?(super)
28
- rescue Eco::API::UseCases::GraphQL::Base::NotImplementedMethod
29
- end
26
+ def process # rubocop:disable Metrics/AbcSize
27
+ self.error = false
28
+ self.exception = nil
29
+
30
+ super if defined?(super)
30
31
 
31
- self.error = false
32
- # this triggers a backup of the tagtree
32
+ # this may trigger a backup of the tagtree
33
33
  self.current_tree ||= live_tree
34
34
 
35
35
  inputs(force_continue: force_continue?) do |input, stage|
36
36
  results[stage] ||= []
37
- sliced_batches(input, desc: stage) do |sliced_input, response, page, pages, done, total|
37
+
38
+ track_mode = batch_tree_track_mode(stage)
39
+
40
+ # yields the result of each batch
41
+ sliced_batches(input, desc: stage, track_tree_mode: track_mode) do |sliced_input, response, page, pages, done, total| # rubocop:disable Metrics/ParameterLists, Layout/LineLength
38
42
  track_current_tree(response&.structure)
39
- page_results = nil
43
+
40
44
  results[stage] << (page_results = request_results_class.new(sliced_input, response))
41
- update_tags_remap_table(page_results, stage)
42
- break if self.error = page_errors?(page_results, page, pages, done, total, stage: stage)
45
+ update_tags_remap_table(page_results, stage, current_tree)
46
+
47
+ self.error = page_errors?(page_results, page, pages, done, total, stage: stage)
48
+ break if error
43
49
  end
44
50
 
45
51
  break if error
46
52
  end
53
+ rescue SystemStackError
54
+ puts $! # rubocop:disable Style/SpecialGlobalVars
55
+ puts caller[0..100]
56
+ raise
47
57
  rescue StandardError => e
48
58
  log(:error) { self.exception ||= e.patch_full_message }
49
59
  raise
50
60
  ensure
51
61
  rescued { self.tags_remap_csv_file = generate_tags_remap_csv }
62
+ rescued { close_handling_tags_remap_csv }
63
+ end
64
+ end
65
+
66
+ # Default tree tacking behaviour
67
+ # @note
68
+ # 1. This aims to optimize the time run
69
+ # 2. Based on update stage, there are differentiated tracking needs
70
+ # @return [Symbol] the tracking mode
71
+ def batch_tree_track_mode(stage)
72
+ case stage
73
+ when :unarchive, :archive
74
+ :once
75
+ when :id, :id_name
76
+ :per_request
77
+ when :insert, :move
78
+ :per_batch
79
+ else
80
+ default_tree_tracking_mode
52
81
  end
53
82
  end
54
83
  end