eco-helpers 3.2.1 → 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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +36 -1
  4. data/eco-helpers.gemspec +3 -3
  5. data/lib/eco/api/common/loaders/config/workflow/mailer.rb +1 -0
  6. data/lib/eco/api/common/people/person_entry.rb +6 -1
  7. data/lib/eco/api/common/people/person_parser.rb +1 -0
  8. data/lib/eco/api/microcases/people/manage/cache.rb +21 -16
  9. data/lib/eco/api/microcases/people/manage/filename.rb +29 -0
  10. data/lib/eco/api/microcases/people/manage/load.rb +6 -48
  11. data/lib/eco/api/microcases/people/manage/load_cache.rb +29 -0
  12. data/lib/eco/api/microcases/people/manage/refresh.rb +5 -3
  13. data/lib/eco/api/microcases/people/manage.rb +4 -0
  14. data/lib/eco/api/organization/tag_tree.rb +11 -4
  15. data/lib/eco/api/session/batch/searcher.rb +3 -3
  16. data/lib/eco/api/session/config/people.rb +23 -20
  17. data/lib/eco/api/usecases/default/locations/tagtree_upload_case.rb +20 -2
  18. data/lib/eco/api/usecases/graphql/helpers/location/base/tree_tracking.rb +14 -7
  19. data/lib/eco/api/usecases/graphql/helpers/location/base.rb +1 -1
  20. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/as_update.rb +0 -1
  21. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_archive.rb +0 -1
  22. data/lib/eco/api/usecases/graphql/helpers/location/command/end_points/optimizations.rb +64 -0
  23. data/lib/eco/api/usecases/graphql/helpers/location/command/end_points.rb +96 -0
  24. data/lib/eco/api/usecases/graphql/helpers/location/command/input_unit_response.rb +69 -0
  25. data/lib/eco/api/usecases/graphql/helpers/location/command/result.rb +11 -10
  26. data/lib/eco/api/usecases/graphql/helpers/location/command/results.rb +120 -63
  27. data/lib/eco/api/usecases/graphql/helpers/location/command.rb +26 -26
  28. data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +195 -37
  29. data/lib/eco/api/usecases/graphql/samples/location/command/results.rb +45 -13
  30. data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +0 -51
  31. data/lib/eco/api/usecases/graphql/samples/location/command/track_changed_ids.rb +6 -14
  32. data/lib/eco/api/usecases/graphql/samples/location/command.rb +1 -1
  33. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/inputable.rb +4 -1
  34. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff.rb +6 -0
  35. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/input.rb +1 -0
  36. data/lib/eco/api/usecases/lib/error_handling.rb +1 -1
  37. data/lib/eco/api/usecases/ooze_samples/helpers/creatable.rb +1 -0
  38. data/lib/eco/api/usecases/ooze_samples/helpers/rescuable.rb +1 -0
  39. data/lib/eco/api/usecases/samples/drivers/cli/sftp_cli.rb +1 -1
  40. data/lib/eco/api/usecases/samples/drivers/sftp_sample.rb +4 -1
  41. data/lib/eco/api/usecases/samples/drivers/url_pull_sample.rb +12 -9
  42. data/lib/eco/api/usecases/samples/people/filters/cli/require_id_cli.rb +5 -0
  43. data/lib/eco/api/usecases/samples/people/filters/cli/require_min_entries_cli.rb +5 -0
  44. data/lib/eco/api/usecases/samples/people/filters/require_id_sample.rb +61 -0
  45. data/lib/eco/api/usecases/samples/people/filters/require_min_entries_sample.rb +20 -0
  46. data/lib/eco/api/usecases/samples/people/filters.rb +7 -0
  47. data/lib/eco/api/usecases/samples/people.rb +12 -0
  48. data/lib/eco/api/usecases/samples.rb +1 -0
  49. data/lib/eco/cli_default/options.rb +5 -0
  50. data/lib/eco/data/hashes/array_diff.rb +14 -4
  51. data/lib/eco/data/locations/node_base/csv_convert.rb +9 -1
  52. data/lib/eco/data/locations/node_base/tag_validations.rb +2 -2
  53. data/lib/eco/data/locations/node_diff/nodes_diff/diffs_tree.rb +10 -3
  54. data/lib/eco/data/locations/node_diff/nodes_diff.rb +6 -0
  55. data/lib/eco/version.rb +1 -1
  56. metadata +23 -13
  57. data/lib/eco/api/usecases/graphql/helpers/location/command/optimizations.rb +0 -84
@@ -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
@@ -1,63 +1,133 @@
1
1
  module Eco::API::UseCases::GraphQL::Helpers::Location::Command
2
+ # Final ressults of the full run.
2
3
  class Results
3
- attr_reader :input, :response
4
+ attr_accessor :draft
4
5
 
5
- def initialize(input, response)
6
- @input = input
7
- @response = response
6
+ def draft_id
7
+ draft&.id
8
8
  end
9
9
 
10
- def stats
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?
15
- msg
10
+ def parent_structure
11
+ draft&.parent_structure
16
12
  end
17
13
 
18
- # Was this configured to force-continue on command error?
19
- def force?
20
- input[:force]
14
+ # @note target parent structure id
15
+ def structure_id
16
+ draft&.structure_id
21
17
  end
22
18
 
23
- # # Offers a summary. If anything went wrong, it's `false`.
24
- # # If everything went right, it's `true`.
25
- # def ok?
26
- # response&.ok
27
- # end
19
+ # @note the result of the draft
20
+ def structure
21
+ draft&.structure
22
+ end
28
23
 
29
- # Overal errors (i.e. ID clashes between different structures)
30
- def error
31
- response&.error
24
+ # @note it receives the final `results` response (when the draft gets published)
25
+ def final_response(response = nil)
26
+ return @response if response.nil?
27
+
28
+ @response = response
29
+ end
30
+
31
+ def final_response?
32
+ !final_response.nil?
32
33
  end
33
34
 
35
+ # @note it only accounts for the errors of the final publishing of the draft.
34
36
  def error?
35
- !!error
37
+ return false unless final_response
38
+
39
+ final_response.error?
36
40
  end
37
41
 
38
42
  def success?
39
- !error? && results.all?(&:success?)
43
+ !error?
44
+ end
45
+
46
+ def applied_commands(with_id_change: false)
47
+ results.select(&:applied?).then do |applied|
48
+ next applied unless with_id_change
49
+
50
+ applied.select do |result|
51
+ next false unless (command = result.command_result_data)
52
+
53
+ command.keys.include?(:newId)
54
+ end
55
+ end
40
56
  end
41
57
 
42
58
  def results
43
- @results ||= input_commands.zip(response_results).each_with_object([]) do |(i, r), results|
59
+ return [] unless final_response
60
+
61
+ @results ||= input_commands.zip(command_results).each_with_object([]) do |(i, r), results|
44
62
  results << Result.new(i, r)
45
63
  end
46
64
  end
47
65
 
66
+ # @note this captures the latest draft
67
+ def add_response(key, input_unit_response)
68
+ self[key] << input_unit_response
69
+ input_unit_response.tap do |iur|
70
+ next if final_response
71
+
72
+ self.draft = iur.draft
73
+ end
74
+ end
75
+
76
+ def[](key)
77
+ responses[key] ||= []
78
+ end
79
+
80
+ def responses
81
+ @responses ||= {}
82
+ end
83
+
84
+ def keys
85
+ responses.keys
86
+ end
87
+
48
88
  def count
49
- results.count
89
+ responses.values.flatten.count
50
90
  end
51
91
 
52
- def input_result(input)
53
- results_by_input[input]
92
+ def input_commands
93
+ keys.each_with_object([]) do |key, commands|
94
+ self[key].each do |input|
95
+ next unless input.is_a?(InputUnitResponse)
96
+
97
+ commands.concat(input.commands)
98
+ end
99
+ end
100
+ end
101
+
102
+ def command_results(response = final_response)
103
+ response = final_response(response)
104
+ return [] unless response
105
+
106
+ response.results || []
107
+ end
108
+
109
+ def stats
110
+ msg = ''
111
+ msg << " * Errored: #{errored.count} #{first_err_str}\n" if errored?
112
+ msg << " * Applied: #{applied.count} #{last_okay_str}\n" if some_applied?
113
+ msg << " * Pending: #{pending.count}\n" if some_pending?
114
+ msg
54
115
  end
55
116
 
56
- def input_idx(input)
57
- results.index(input_result(input))
117
+ # Overal errors (i.e. ID clashes between different structures)
118
+ def error
119
+ response&.error
58
120
  end
59
121
 
60
- def idx(result)
122
+ # def input_result(input)
123
+ # results_by_input[input]
124
+ # end
125
+
126
+ # def input_idx(input)
127
+ # results.index(input_result(input))
128
+ # end
129
+
130
+ def result_idx(result)
61
131
  results.index(result)
62
132
  end
63
133
 
@@ -65,24 +135,28 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
65
135
  @errored ||= results.select(&:error?)
66
136
  end
67
137
 
138
+ def applied
139
+ @applied ||= results.select(&:applied?)
140
+ end
141
+
142
+ def pending
143
+ @pending ||= results.select(&:pending?)
144
+ end
145
+
68
146
  def errored?
69
147
  errored.any?
70
148
  end
71
149
 
150
+ def applied?
151
+ results.all?(&:applied?)
152
+ end
153
+
72
154
  def first_errored
73
155
  errored.first
74
156
  end
75
157
 
76
158
  def first_errored_idx
77
- idx(first_errored)
78
- end
79
-
80
- def applied
81
- @applied ||= results.select(&:applied?)
82
- end
83
-
84
- def applied?
85
- results.all?(&:applied?)
159
+ result_idx(first_errored)
86
160
  end
87
161
 
88
162
  def some_applied?
@@ -94,11 +168,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
94
168
  end
95
169
 
96
170
  def last_applied_idx
97
- idx(last_applied)
98
- end
99
-
100
- def pending
101
- @pending ||= results.select(&:pending?)
171
+ result_idx(last_applied)
102
172
  end
103
173
 
104
174
  def some_pending?
@@ -107,34 +177,21 @@ module Eco::API::UseCases::GraphQL::Helpers::Location::Command
107
177
 
108
178
  private
109
179
 
110
- def results_by_input
111
- @results_by_input ||= results.each_with_object({}) do |r, h|
112
- h[r.input] = r
113
- end
114
- end
115
-
116
- def input_commands
117
- input[:commands]
118
- end
119
-
120
- def response_results
121
- response&.results || []
122
- end
180
+ # def results_by_input
181
+ # @results_by_input ||= results.each_with_object({}) do |res, h|
182
+ # h[res.input] = res
183
+ # end
184
+ # end
123
185
 
124
186
  # First error is relevant only if request was NOT forced to continue.
125
187
  def first_err_str
126
- return '' if force?
127
-
128
- pre_str = 'stopped on node'
129
- pre_str = 'first error' unless force?
188
+ pre_str = 'first error'
130
189
 
131
190
  "(#{pre_str}: '#{first_errored&.node_id}' - idx: #{first_errored_idx})"
132
191
  end
133
192
 
134
193
  # Last successfully applied is relevant only if request was NOT forced to continue.
135
194
  def last_okay_str
136
- return '' if force?
137
-
138
195
  "(last node done: '#{last_applied&.node_id}' - idx: #{last_applied_idx})"
139
196
  end
140
197
  end
@@ -3,25 +3,10 @@ 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
- require_relative 'command/optimizations'
7
- include Eco::API::UseCases::GraphQL::Helpers::Location::Command::Optimizations
8
-
9
- # Actual GraphQL request
10
- def apply_commands(input, &block)
11
- graphql.locationStructure.applyCommands(input: input, &block)
12
- end
13
-
14
- # With given the commands, it generates the input of the endpoint mutation.
15
- # @param commands [Array<Hash>]
16
- def input(commands, force_continue: force_continue?)
17
- {
18
- clientMutationId: '',
19
- id: target_structure_id,
20
- force: force_continue,
21
- commands: commands
22
- }
23
- end
6
+ require_relative 'command/end_points'
7
+ include Eco::API::UseCases::GraphQL::Helpers::Location::Command::EndPoints
24
8
 
9
+ # @note it does the API call.
25
10
  # @param track_tree_mode [Symbol] when should updates happen
26
11
  # @return see #with_sliced_input
27
12
  def sliced_batches(
@@ -46,17 +31,31 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
46
31
  msg << "with #{count} commands (done #{done} of #{total})..."
47
32
  log(:info) { msg }
48
33
 
49
- response = nil
50
34
  unless simulate? && !options.dig(:requests, :backup)
51
- backup(sliced_input, type: "tree_update_#{desc}_request_#{page}_of_#{pages}")
35
+ backup(
36
+ sliced_input,
37
+ type: "tree_update_#{desc}_request_#{page}_of_#{pages}"
38
+ )
52
39
  end
53
40
 
54
- if simulate?
55
- log(:info) { sliced_input.pretty_inspect } if page < 3
56
- else
57
- curr_block = scope_commands_block(page, pages, track_tree_mode: track_tree_mode)
58
- response = apply_commands(sliced_input, &curr_block)
59
- backup(response, type: "tree_update_#{desc}_response_#{page}_of_#{pages}")
41
+ if simulate? && page < 3
42
+ log(:info) {
43
+ JSON.pretty_generate(sliced_input)
44
+ }
45
+ end
46
+
47
+ # fetch structure? (by using default, when curr_block == nil)
48
+ response = apply_commands(
49
+ sliced_input,
50
+ final: (page == pages),
51
+ track_tree_mode: track_tree_mode
52
+ )
53
+
54
+ unless simulate?
55
+ backup(
56
+ response,
57
+ type: "tree_update_#{desc}_response_#{page}_of_#{pages}"
58
+ )
60
59
  end
61
60
 
62
61
  done += count
@@ -88,6 +87,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
88
87
  end
89
88
 
90
89
  require_relative 'command/result'
90
+ require_relative 'command/input_unit_response'
91
91
  require_relative 'command/results'
92
92
  require_relative 'command/diff'
93
93
  require_relative 'command/diffs'