eco-helpers 2.6.4 → 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +95 -0
  4. data/CHANGELOG.md +139 -2
  5. data/Rakefile +13 -7
  6. data/eco-helpers.gemspec +2 -2
  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/exception.rb +11 -13
  40. data/lib/eco/api/error.rb +32 -20
  41. data/lib/eco/api/organization/node_classifications.rb +82 -0
  42. data/lib/eco/api/organization/policy_groups.rb +4 -6
  43. data/lib/eco/api/organization/tag_tree.rb +169 -93
  44. data/lib/eco/api/organization.rb +1 -0
  45. data/lib/eco/api/session/batch/job.rb +1 -1
  46. data/lib/eco/api/session/config/tagtree.rb +41 -23
  47. data/lib/eco/api/session/config/workflow.rb +113 -88
  48. data/lib/eco/api/session/config.rb +6 -0
  49. data/lib/eco/api/session.rb +51 -29
  50. data/lib/eco/api/usecases/base_io.rb +28 -25
  51. data/lib/eco/api/usecases/default/locations/cli/tagtree_extract_cli.rb +7 -2
  52. data/lib/eco/api/usecases/default/locations/cli/tagtree_upload_cli.rb +21 -0
  53. data/lib/eco/api/usecases/default/locations/csv_to_tree_case.rb +3 -3
  54. data/lib/eco/api/usecases/default/locations/tagtree_extract_case.rb +54 -23
  55. data/lib/eco/api/usecases/default/locations/tagtree_upload_case.rb +87 -0
  56. data/lib/eco/api/usecases/default/locations.rb +1 -0
  57. data/lib/eco/api/usecases/default/people/analyse_people_case.rb +60 -56
  58. data/lib/eco/api/usecases/default/people/change_email_case.rb +8 -9
  59. data/lib/eco/api/usecases/default/people/clean_unknown_tags_case.rb +13 -11
  60. data/lib/eco/api/usecases/default/people/clear_abilities_case.rb +2 -2
  61. data/lib/eco/api/usecases/default/people/org_data_convert_case.rb +25 -27
  62. data/lib/eco/api/usecases/default/people/refresh_case.rb +2 -2
  63. data/lib/eco/api/usecases/default/people/reinvite_trans_case.rb +1 -1
  64. data/lib/eco/api/usecases/default/people/reinvite_trans_cli.rb +0 -1
  65. data/lib/eco/api/usecases/default/people/restore_db_case.rb +39 -34
  66. data/lib/eco/api/usecases/default/people/set_default_tag_case.rb +19 -15
  67. data/lib/eco/api/usecases/default/people/supers_cyclic_identify_case.rb +16 -12
  68. data/lib/eco/api/usecases/default_cases/hris_case.rb +17 -15
  69. data/lib/eco/api/usecases/default_cases/samples/sftp_case.rb +30 -16
  70. data/lib/eco/api/usecases/graphql/base.rb +5 -3
  71. data/lib/eco/api/usecases/graphql/helpers/base/case_env.rb +4 -1
  72. data/lib/eco/api/usecases/graphql/helpers/base/graphql_env.rb +14 -0
  73. data/lib/eco/api/usecases/graphql/helpers/base.rb +5 -4
  74. data/lib/eco/api/usecases/graphql/helpers/location/base/classifications_parser.rb +60 -0
  75. data/lib/eco/api/usecases/graphql/helpers/location/base/tree_tracking.rb +72 -0
  76. data/lib/eco/api/usecases/graphql/helpers/location/base.rb +25 -59
  77. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/as_update.rb +59 -0
  78. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/compare.rb +49 -0
  79. data/lib/eco/api/usecases/graphql/helpers/location/command/diff.rb +11 -0
  80. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/commandable.rb +46 -0
  81. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_archive.rb +23 -0
  82. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_unarchive.rb +65 -0
  83. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable.rb +49 -0
  84. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/sortable/relation_safe_sort.rb +119 -0
  85. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/sortable.rb +59 -0
  86. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages.rb +82 -0
  87. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs.rb +20 -0
  88. data/lib/eco/api/usecases/graphql/helpers/location/command/optimizations.rb +84 -0
  89. data/lib/eco/api/usecases/graphql/helpers/location/command/result.rb +4 -4
  90. data/lib/eco/api/usecases/graphql/helpers/location/command/results.rb +24 -12
  91. data/lib/eco/api/usecases/graphql/helpers/location/command.rb +21 -24
  92. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap/tags_map.rb +1 -1
  93. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap/tags_set.rb +10 -11
  94. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap.rb +8 -9
  95. data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +41 -12
  96. data/lib/eco/api/usecases/graphql/samples/location/command/results.rb +11 -80
  97. data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +89 -0
  98. data/lib/eco/api/usecases/graphql/samples/location/command/service.rb +6 -0
  99. data/lib/eco/api/usecases/graphql/samples/location/command/track_changed_ids.rb +89 -0
  100. data/lib/eco/api/usecases/graphql/samples/location/command.rb +3 -0
  101. data/lib/eco/api/usecases/graphql/samples/location/service/base.rb +9 -0
  102. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/heading.rb +18 -0
  103. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/inputable.rb +53 -0
  104. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing/classifications.rb +34 -0
  105. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing/helpers.rb +28 -0
  106. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing.rb +46 -0
  107. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible.rb +38 -0
  108. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff.rb +105 -0
  109. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/discarded.rb +16 -0
  110. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/input.rb +15 -0
  111. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/node_attr_maps.rb +22 -0
  112. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/parser.rb +45 -0
  113. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter.rb +36 -0
  114. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/output.rb +56 -0
  115. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list.rb +41 -0
  116. data/lib/eco/api/usecases/graphql/samples/location/service.rb +8 -0
  117. data/lib/eco/api/usecases/graphql/samples/location.rb +1 -0
  118. data/lib/eco/api/usecases/graphql/utils/sftp.rb +96 -36
  119. data/lib/eco/api/usecases/ooze_cases/export_register_case.rb +8 -6
  120. data/lib/eco/api/usecases/ooze_samples/helpers/creatable.rb +4 -3
  121. data/lib/eco/api/usecases/ooze_samples/helpers/exportable_ooze.rb +39 -25
  122. data/lib/eco/api/usecases/ooze_samples/helpers/exportable_register.rb +13 -15
  123. data/lib/eco/api/usecases/ooze_samples/helpers/filters.rb +50 -21
  124. data/lib/eco/api/usecases/ooze_samples/helpers/ooze_handlers.rb +21 -11
  125. data/lib/eco/api/usecases/ooze_samples/helpers/rescuable.rb +2 -0
  126. data/lib/eco/api/usecases/ooze_samples/helpers/shortcuts.rb +49 -43
  127. data/lib/eco/api/usecases/ooze_samples/ooze_base_case.rb +17 -19
  128. data/lib/eco/api/usecases/ooze_samples/register_export_case.rb +48 -43
  129. data/lib/eco/api/usecases/ooze_samples/register_update_case.rb +33 -34
  130. data/lib/eco/api/usecases/ooze_samples/target_oozes_update_case.rb +8 -10
  131. data/lib/eco/api/usecases.rb +0 -1
  132. data/lib/eco/cli/config/use_cases.rb +31 -29
  133. data/lib/eco/cli_default/workflow.rb +13 -14
  134. data/lib/eco/csv/table.rb +34 -25
  135. data/lib/eco/data/hashes/array_diff.rb +24 -35
  136. data/lib/eco/data/hashes/diff_result/meta.rb +131 -0
  137. data/lib/eco/data/hashes/diff_result.rb +65 -57
  138. data/lib/eco/data/hashes/sanke_camel_indifferent_access.rb +278 -0
  139. data/lib/eco/data/hashes.rb +1 -1
  140. data/lib/eco/data/locations/convert.rb +1 -1
  141. data/lib/eco/data/locations/node_base/csv_convert.rb +19 -9
  142. data/lib/eco/data/locations/node_base/parsing.rb +4 -2
  143. data/lib/eco/data/locations/node_base/treeify.rb +149 -132
  144. data/lib/eco/data/locations/node_base.rb +15 -4
  145. data/lib/eco/data/locations/node_diff/accessors.rb +13 -5
  146. data/lib/eco/data/locations/node_diff/nodes_diff/clustered_treeify.rb +101 -0
  147. data/lib/eco/data/locations/node_diff/nodes_diff/diffs_tree.rb +99 -0
  148. data/lib/eco/data/locations/node_diff/{selectors.rb → nodes_diff/selectors.rb} +1 -1
  149. data/lib/eco/data/locations/node_diff/nodes_diff.rb +50 -35
  150. data/lib/eco/data/locations/node_diff.rb +45 -17
  151. data/lib/eco/data/locations/node_level/parsing.rb +15 -21
  152. data/lib/eco/data/locations/node_level.rb +66 -22
  153. data/lib/eco/data/locations/node_plain/parsing.rb +1 -1
  154. data/lib/eco/data/locations/node_plain.rb +60 -7
  155. data/lib/eco/data/strings/camel_case.rb +35 -0
  156. data/lib/eco/data/strings/snake_case.rb +18 -0
  157. data/lib/eco/data/strings.rb +8 -0
  158. data/lib/eco/data.rb +1 -0
  159. data/lib/eco/language/auxiliar_logger.rb +7 -5
  160. data/lib/eco/language/methods/call_detector.rb +11 -0
  161. data/lib/eco/language/methods/dsl_able.rb +7 -1
  162. data/lib/eco/language/methods.rb +2 -1
  163. data/lib/eco/language/models/collection.rb +23 -25
  164. data/lib/eco/language/models/parser_serializer.rb +24 -5
  165. data/lib/eco/version.rb +1 -1
  166. data/lib/eco-helpers.rb +0 -1
  167. metadata +52 -7
  168. data/lib/eco/data/hashes/diff_meta.rb +0 -52
data/lib/eco/api/error.rb CHANGED
@@ -10,56 +10,65 @@ module Eco
10
10
  end
11
11
 
12
12
  @str_err = "api error on the server reply"
13
- @match = /.*/
13
+ @match = /.*/
14
14
  #RxValidId = /[a-f0-9]{24}/
15
15
 
16
-
17
16
  class Unclassified < Eco::API::Error
18
17
  @str_error = "Unclassified error message"
19
18
  @match = /.*/
20
19
  end
21
20
 
22
21
  class InternalServerError < Eco::API::Error
23
- @str_err = "Internal Server Error"
22
+ @str_err = "Internal Server Error"
24
23
  @match = /#{@str_err}/
25
24
  end
25
+
26
26
  class UnknownPersonId < Eco::API::Error
27
27
  @str_err = "Unknown person id"
28
28
  @match = /Cannot find person with id (.*)/
29
29
  end
30
+
30
31
  class ExternalIdTaken < Eco::API::Error
31
- @str_err = "external ID already taken"
32
+ @str_err = "external ID already taken"
32
33
  @match = /#{@str_err}/
33
34
  end
35
+
34
36
  class EmailMissing < Eco::API::Error
35
37
  @str_err = "missing email for account creation"
36
38
  @match = /#{@str_err}/
37
39
  end
40
+
38
41
  class EmailInvalid < Eco::API::Error
39
42
  @str_err = "Email is invalid"
40
43
  @match = /#{@str_err}/
41
44
  end
45
+
42
46
  class EmailTaken < Eco::API::Error
43
- @str_err = "user email already taken"
44
- @str_err2 = "Email is already taken"
45
- @match = /(?:#{@str_err}|#{@str_err2})/
47
+ @str_err = "user email already taken"
48
+ @str_err_2 = "Email is already taken"
49
+ @match = /(?:#{@str_err}|#{@str_err_2})/
46
50
  end
51
+
47
52
  class SupervisorNotFound < Eco::API::Error
48
53
  @str_err = "Supervisor not found"
49
54
  @match = /Supervisor (.*?) not found/
50
55
  end
56
+
51
57
  class CyclicSupervisor < Eco::API::Error
52
58
  @str_err = "Supervisor is cyclic!"
53
59
  @match = /#{@str_err}/
54
60
  end
61
+
55
62
  class SchemaNotFound < Eco::API::Error
56
63
  @str_err = "Schema not found"
57
64
  @match = /Schema (.*?) not found/
58
65
  end
66
+
59
67
  class InvalidObjectId < Eco::API::Error
60
68
  @str_err = "Invalid ObjectId."
61
69
  @match = /'(.*?)' is an invalid ObjectId./
62
70
  end
71
+
63
72
  class UnknownField < Eco::API::Error
64
73
  @str_err = "Unknown field."
65
74
  @match = /(.+?) is an unknown field/
@@ -68,13 +77,15 @@ module Eco
68
77
  @str_err = "Unknown core field."
69
78
  @match = /(.+?) is an unknown field/
70
79
  end
80
+
71
81
  class UnknownAccountField < UnknownField
72
82
  @str_err = "Unknown account field."
73
- @match = /account \> (.+?) is an unknown field/
83
+ @match = /account > (.+?) is an unknown field/
74
84
  end
85
+
75
86
  class UnknownDetailsField < UnknownField
76
87
  @str_err = "Unknown details field."
77
- @match = /details \> (.+?) is an unknown field/
88
+ @match = /details > (.+?) is an unknown field/
78
89
  end
79
90
  end
80
91
 
@@ -82,9 +93,9 @@ module Eco
82
93
  def descendants(direct: false)
83
94
  ObjectSpace.each_object(::Class).select do |klass|
84
95
  klass < self
85
- end.sort do |k1, k2|
86
- next -1 if k2 < k1
87
- next 1 if k1 < k2
96
+ end.sort do |k_1, k_2|
97
+ next -1 if k_2 < k_1
98
+ next 1 if k_1 < k_2
88
99
  0
89
100
  end.tap do |siblings|
90
101
  siblings.delete(Unclassified)
@@ -97,7 +108,7 @@ module Eco
97
108
  end
98
109
 
99
110
  def descendants?(direct: false)
100
- descendants(direct: direct).length > 0
111
+ descendants(direct: direct).length.positive?
101
112
  end
102
113
 
103
114
  def err_match?(err_msg)
@@ -106,15 +117,16 @@ module Eco
106
117
 
107
118
  def get_type(err_msg, first: true)
108
119
  type = nil
120
+
109
121
  descendants(direct: true).reverse.each do |klass|
110
- if klass.err_match?(err_msg)
111
- type = klass
112
- if klass.descendants?(direct: true)
113
- type = klass.get_type(err_msg, first: false) || type
114
- end
115
- end
122
+ next unless klass.err_match?(err_msg)
123
+ type = klass
124
+ next unless klass.descendants?(direct: true)
125
+ type = klass.get_type(err_msg, first: false) || type
116
126
  end
127
+
117
128
  return type unless first
129
+
118
130
  type || Unclassified
119
131
  end
120
132
 
@@ -138,7 +150,7 @@ module Eco
138
150
  end
139
151
 
140
152
  def built_error
141
- str ||= msg
153
+ msg
142
154
  end
143
155
  end
144
156
  end
@@ -0,0 +1,82 @@
1
+ module Eco
2
+ module API
3
+ module Organization
4
+ class NodeClassifications < Eco::Language::Models::Collection
5
+ # build the shortcuts of Collection
6
+ attr_collection :id, :name # , :active
7
+
8
+ def initialize(types = [], klass: Ecoportal::API::GraphQL::Base::LocationClassificationType) # rubocop:disable Lint/UnusedMethodArgument
9
+ @klass = Ecoportal::API::GraphQL::Base::LocationClassificationType
10
+ @caches_init = false
11
+ super(types, klass: @klass)
12
+ init_caches
13
+ end
14
+
15
+ def ids
16
+ @items.map(&:id)
17
+ end
18
+
19
+ def names
20
+ @items.map(&:name)
21
+ end
22
+
23
+ def to_id(name)
24
+ case name
25
+ when Enumerable
26
+ name.map do |n|
27
+ type(n)&.id
28
+ end.compact
29
+ else
30
+ type(name)&.id
31
+ end
32
+ end
33
+
34
+ def to_name(id)
35
+ case id
36
+ when Enumerable
37
+ id.map do |n|
38
+ type(n)&.name
39
+ end.compact
40
+ else
41
+ type(id)&.name
42
+ end
43
+ end
44
+
45
+ def type(id_name)
46
+ self[id_name]
47
+ end
48
+
49
+ def [](id_name)
50
+ @by_id[type_id(id_name)]
51
+ end
52
+
53
+ private
54
+
55
+ def type_name(id_name)
56
+ key = treat_classication(id_name)
57
+ val = (@by_id[key] || @by_name[key])&.name
58
+ treat_classication(val)
59
+ end
60
+
61
+ def type_id(id_name)
62
+ key = treat_classication(id_name)
63
+ val = (@by_name[key] || @by_id[key])&.id
64
+ treat_classication(val)
65
+ end
66
+
67
+ def treat_classication(value)
68
+ return value unless value.is_a?(String)
69
+ value.strip.gsub(/\W+/, '').downcase
70
+ end
71
+
72
+ def init_caches
73
+ return if @caches_init
74
+
75
+ @by_id = @items.to_h { |nc| [treat_classication(nc.id), nc] }
76
+ @by_name = @items.to_h { |nc| [treat_classication(nc.name), nc] }
77
+ @caches_init = true
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,7 +1,7 @@
1
1
  module Eco
2
2
  module API
3
3
  module Organization
4
- class PolicyGroups < Eco::Language::Models::Collection
4
+ class PolicyGroups < Eco::Language::Models::Collection
5
5
  # build the shortcuts of Collection
6
6
  attr_collection :id, :name
7
7
 
@@ -40,7 +40,6 @@ module Eco
40
40
  else
41
41
  policy_group(id)&.name
42
42
  end
43
-
44
43
  end
45
44
 
46
45
  def policy_group(id_name)
@@ -61,7 +60,7 @@ module Eco
61
60
 
62
61
  if !non_custom_not_used && preserve_custom
63
62
  initial_custom = initial - non_custom
64
- final = final + initial_custom
63
+ final += initial_custom
65
64
  end
66
65
 
67
66
  new_pg_ids = final - initial
@@ -81,11 +80,10 @@ module Eco
81
80
 
82
81
  def init_caches
83
82
  return if @caches_init
84
- @by_id = self.map { |pg| [pg.id, pg] }.to_h
85
- @by_name = self.map { |pg| [pg.name&.downcase, pg] }.to_h
83
+ @by_id = map { |pg| [pg.id, pg] }.to_h
84
+ @by_name = map { |pg| [pg.name&.downcase, pg] }.to_h
86
85
  @caches_init = true
87
86
  end
88
-
89
87
  end
90
88
  end
91
89
  end
@@ -6,23 +6,27 @@ module Eco
6
6
  # This does not allow to capture the `name` and `id` of the locations
7
7
  # structure itself into the json storing model.
8
8
  class TagTree
9
- HEADER = [
10
- 'id', 'name', 'weight', 'parent_id',
11
- 'archived', 'archived_token'
9
+ HEADER = %w[
10
+ id name weight parent_id
11
+ archived archived_token
12
+ classifications classification_names
13
+ level
12
14
  ].freeze
13
15
 
16
+ include Enumerable
17
+
18
+ attr_reader :source
19
+
14
20
  attr_accessor :id
15
21
  alias_method :tag, :id
22
+
16
23
  attr_accessor :name, :weight
17
24
  attr_accessor :archived, :archived_token
25
+ attr_accessor :classifications, :classification_names
18
26
 
19
27
  attr_reader :parent
20
- attr_reader :nodes, :children_count
21
- attr_reader :depth, :path
22
-
23
- attr_reader :source
24
-
25
- include Enumerable
28
+ attr_reader :nodes
29
+ attr_reader :depth
26
30
 
27
31
  # @example Node format:
28
32
  # {"tag": "NODE NAME", "nodes": subtree}
@@ -34,33 +38,32 @@ module Eco
34
38
  # ]}]
35
39
  # tree = TagTree.new(tree.to_json)
36
40
  # @param tagtree [String] representation of the tagtree in json.
37
- def initialize(tagtree = [], name: nil, id: nil, depth: -1, path: [], parent: nil, _weight: nil)
38
- @depth = depth
39
- @parent = parent
41
+ def initialize(
42
+ tagtree = [],
43
+ name: nil, id: nil,
44
+ depth: -1, path: [],
45
+ parent: nil, _weight: nil # rubocop:disable Lint/UnderscorePrefixedVariableName
46
+ )
40
47
 
41
- case tagtree
42
- when String
43
- @source = JSON.parse(tagtree)
44
- else
45
- @source = tagtree
46
- end
47
- raise ArgumentError, "You are trying to initialize a TagTree with a null tagtree" if !@source
48
+ @source = parse_source_input(tagtree)
49
+
50
+ msg = "You are trying to initialize a TagTree with a null tagtree"
51
+ raise ArgumentError, msg unless source
52
+
53
+ @parent = parent
54
+ @depth = depth
55
+ @path = path || []
48
56
 
49
- if @source.is_a?(Array)
57
+ if source.is_a?(Array)
50
58
  @id = id
51
59
  @name = name
52
- @raw_nodes = @source
60
+ @raw_nodes = source
53
61
  else
54
- @id = @source.values_at('tag', 'id').compact.first&.upcase
55
- @name = @source['name']
56
- @archived = @source['archived'] || false
57
- @archived_token = @source['archived_token']
58
- @source['weight'] = @weight = @source['weight'] || _weight
59
- @raw_nodes = @source['nodes'] || []
62
+ source['weight'] ||= _weight
63
+ init_node
60
64
  end
61
65
 
62
- @path = path || []
63
- @path.push(@id) unless top?
66
+ self.path.push(self.id) unless top?
64
67
 
65
68
  @nodes = @raw_nodes.map.with_index do |cnode, idx|
66
69
  self.class.new(cnode, depth: depth + 1, path: @path.dup, parent: self, _weight: idx)
@@ -84,18 +87,19 @@ module Eco
84
87
  end
85
88
 
86
89
  # @return [Array] with the differences
87
- def diff(tagtree, differences: {}, level: 0, **options)
90
+ def diff(tagtree, _differences: {}, _level: 0, **options)
88
91
  require 'hashdiff'
89
- Hashdiff.diff(self.as_json, tagtree.as_json, **options.slice(:array_path, :similarity, :use_lcs))
92
+ Hashdiff.diff(as_json, tagtree.as_json, **options.slice(:array_path, :similarity, :use_lcs))
90
93
  end
91
94
 
92
95
  # It generates a merged tagtree out of two sources
93
96
  # @note it merges the first level nodes (and their children) as it comes
94
97
  # @return [Eco::API::Organization::TagTree] result of merging both trees
95
98
  def merge(other)
96
- raise ArgumentError, "Expecting Eco::API::Organization::TagTree. Given: #{other.class}" unless other.is_a?(Eco::API::Organization::TagTree)
97
- mid = [self.id, other.id].join('|')
98
- mname = [self.name, other.name].join('|')
99
+ msg = "Expecting Eco::API::Organization::TagTree. Given: #{other.class}"
100
+ raise ArgumentError, msg unless other.is_a?(Eco::API::Organization::TagTree)
101
+ mid = [id, other.id].join('|')
102
+ mname = [name, other.name].join('|')
99
103
  self.class.new(as_json | other.as_json, id: mid, name: mname)
100
104
  end
101
105
 
@@ -187,7 +191,11 @@ module Eco
187
191
  # @param include_archived [Boolean] whether it should include archived nodes.
188
192
  # @param max_depth [Boolean] up to what level `depth` nodes should be included.
189
193
  # @return [Array[Hash]] where `Hash` is a `node` (i.e. `{"tag" => TAG, "nodes": Array[Hash]}`)
190
- def as_json(include_children: true, include_archived: true, max_depth: total_depth, &block)
194
+ def as_json( # rubocop:disable Metrics/AbcSize
195
+ include_children: true, include_archived: true,
196
+ max_depth: total_depth,
197
+ &block
198
+ )
191
199
  max_depth ||= total_depth
192
200
  return nil if max_depth < depth
193
201
  return [] if top? && !include_children
@@ -202,17 +210,22 @@ module Eco
202
210
  max_depth: max_depth
203
211
  }
204
212
  children_json = child_nodes.map {|nd| nd.as_json(**kargs, &block)}.compact
205
- end
206
213
 
207
- if top?
208
- children_json
209
- else
210
- values = [id, name, weight, parent_id, archived, archived_token]
211
- node_json = self.class::HEADER.zip(values).to_h
212
- node_json["nodes"] = children_json if include_children
213
- node_json = yield(node_json, self) if block_given?
214
- node_json
214
+ return children_json if top?
215
215
  end
216
+
217
+ values = [
218
+ id, name,
219
+ weight, parent_id,
220
+ archived, archived_token,
221
+ classifications.dup,
222
+ classification_names.dup,
223
+ depth + 1
224
+ ]
225
+ node_json = self.class::HEADER.zip(values).to_h
226
+ node_json["nodes"] = children_json if include_children
227
+ node_json = yield(node_json, self) if block_given?
228
+ node_json
216
229
  end
217
230
 
218
231
  # Returns a plain list form of hash nodes.
@@ -233,11 +246,9 @@ module Eco
233
246
 
234
247
  # @return [Integer] the highest `depth` of all the children.
235
248
  def total_depth
236
- @total_depth ||= if has_children?
237
- deepest_node = nodes.max_by do |nd|
238
- nd.total_depth
239
- end
240
- deepest_node.total_depth
249
+ @total_depth ||=
250
+ if children?
251
+ nodes.max_by(&:total_depth)&.total_depth
241
252
  else
242
253
  depth
243
254
  end
@@ -245,20 +256,21 @@ module Eco
245
256
 
246
257
  # @return [Integer] if there's only top level.
247
258
  def flat?
248
- self.total_depth <= 0
259
+ total_depth <= 0
249
260
  end
250
261
 
251
262
  # Gets all the tags of the current node tree.
252
263
  # @note
253
264
  # - this will include the upper level tag(s) as well
254
265
  # - to get all but the upper level tag(s) use `subtags` method instead
255
- # @param depth [Integer] if empty, returns the list of tag nodes of that level. Otherwise the list of tag nodes of the entire subtree.
266
+ # @param depth [Integer] if empty, returns the list of tag nodes of that level.
267
+ # Otherwise the list of tag nodes of the entire subtree.
256
268
  # @return [Array<String>]
257
269
  def tags(depth: nil)
258
- if !depth || depth < 0
270
+ if !depth || depth&.negative?
259
271
  @hash_tags.keys
260
272
  else
261
- @hash_tags.select do |t, n|
273
+ @hash_tags.select do |_t, n|
262
274
  n.depth == depth
263
275
  end.keys
264
276
  end
@@ -280,8 +292,8 @@ module Eco
280
292
  # Returns all the tags with no children
281
293
  # @return [Array<String>]
282
294
  def leafs
283
- tags.select do |tag|
284
- !node(tag).has_children?
295
+ tags.reject do |tag|
296
+ node(tag).children?
285
297
  end
286
298
  end
287
299
 
@@ -291,8 +303,8 @@ module Eco
291
303
  end
292
304
 
293
305
  # @return [Boolean] it has subnodes
294
- def has_children?
295
- children_count > 0
306
+ def children?
307
+ children_count&.positive?
296
308
  end
297
309
 
298
310
  # Verifies if a tag exists in the tree.
@@ -314,7 +326,7 @@ module Eco
314
326
  # @param list [Array<String>] source tags.
315
327
  # @return [Array<String>]
316
328
  def filter_tags(list)
317
- return [] unless list && list.is_a?(Array)
329
+ return [] unless list.is_a?(Array)
318
330
  list.select {|str| tag?(str)}
319
331
  end
320
332
 
@@ -324,7 +336,7 @@ module Eco
324
336
  # @param key [String] tag to find the path to.
325
337
  # @return [Array<String>]
326
338
  def path(key = nil)
327
- return @path.dup if !key
339
+ return @path.dup unless key
328
340
  @hash_paths[key.upcase].dup
329
341
  end
330
342
 
@@ -341,24 +353,32 @@ module Eco
341
353
  # original = ["SYDNEY", "RISK"]
342
354
  # final = ["MELBOURNE", "EVENT"]
343
355
  #
344
- # tree.user_tags(initial: original, final: final) # out: ["MELBOURNE", "RISK"]
345
- # tree.user_tags(initial: original, final: final, preserve_custom: false) # out: ["MELBOURNE"]
346
- # tree.user_tags(initial: original, final: final, add_custom: true) # out: ["MELBOURNE", "RISK", "EVENT"]
347
- # tree.user_tags(initial: original, final: final, preserve_custom: false, add_custom: true) # out: ["MELBOURNE", "EVENT"]
356
+ # tree.user_tags(initial: original, final: final)
357
+ # # out: ["MELBOURNE", "RISK"]
358
+ # tree.user_tags(initial: original, final: final, preserve_custom: false)
359
+ # # out: ["MELBOURNE"]
360
+ # tree.user_tags(initial: original, final: final, add_custom: true)
361
+ # # out: ["MELBOURNE", "RISK", "EVENT"]
362
+ # tree.user_tags(initial: original, final: final, preserve_custom: false, add_custom: true)
363
+ # # out: ["MELBOURNE", "EVENT"]
348
364
  #
349
365
  # @param initial [Array<String>] original tags a person has in their account.
350
- # @param final [Array<String>] target tags the person should have in their account afterwards.
351
- # @param preserve_custom [Boolean] indicates if original tags that are not in the tree should be added/preserved.
352
- # @param add_custom [Boolean] indicates if target tags that are not in the tree should be really added.
366
+ # @param final [Array<String>] target tags the person should have in
367
+ # their account afterwards.
368
+ # @param preserve_custom [Boolean] indicates if original tags
369
+ # that are not in the tree should be added/preserved.
370
+ # @param add_custom [Boolean] indicates if target tags that are
371
+ # not in the tree should be really added.
353
372
  # @return [Array<String>] with the treated final tags.
354
373
  def user_tags(initial: [], final: [], preserve_custom: true, add_custom: false)
355
374
  initial = [initial].flatten.compact
356
375
  final = [final].flatten.compact
357
376
  raise "Expected Array for initial: and final:" unless initial.is_a?(Array) && final.is_a?(Array)
358
- final = filter_tags(final) unless add_custom
359
- custom = initial - filter_tags(initial)
360
- final = final + custom if preserve_custom
361
- new_tags = final - initial
377
+
378
+ final = filter_tags(final) unless add_custom
379
+ custom = initial - filter_tags(initial)
380
+ final |= custom if preserve_custom
381
+ new_tags = final - initial
362
382
  # keep same order as they where
363
383
  (initial & final) + new_tags
364
384
  end
@@ -372,52 +392,108 @@ module Eco
372
392
  # @param [Array<String>] values list of tags.
373
393
  # @return [String] default tag.
374
394
  def default_tag(*values)
375
- values = filter_tags(values)
376
- nodes = []; ddepth = -1
377
- values.each do |tag|
378
- raise("Couldn't find the node of #{tag} in the tag-tree definition") unless cnode = node(tag)
379
-
380
- if cnode.depth > ddepth
381
- nodes = [cnode]
382
- ddepth = cnode.depth
383
- elsif cnode.depth == ddepth
384
- nodes.push(cnode)
395
+ default_tag = nil
396
+ values = filter_tags(values)
397
+ tnodes, ddepth = get_deepest_nodes_among_tags(*values)
398
+
399
+ unless tnodes.empty?
400
+ common = tnodes.reduce(tags.reverse) do |com, cnode|
401
+ com & cnode.path.reverse
385
402
  end
403
+ default_tag = common.first if common.any? && ddepth&.positive?
386
404
  end
387
405
 
388
- default_tag = nil
389
- if nodes.length > 1
390
- common = nodes.reduce(self.tags.reverse) {|com, cnode| com & cnode.path.reverse}
391
- default_tag = common.first if common.length > 0 && ddepth > 0
392
- end
393
- default_tag ||= nodes.first&.tag if (ddepth > 0) || flat?
406
+ default_tag ||= tnodes.first&.tag if ddepth&.positive? || flat?
394
407
  default_tag
395
408
  end
396
409
 
397
410
  protected
398
411
 
412
+ attr_reader :hash_paths
413
+
399
414
  def hash
400
415
  @hash_tags
401
416
  end
402
417
 
403
- def hash_paths
404
- @hash_paths
418
+ private
419
+
420
+ def parse_source_input(value)
421
+ return value unless value.is_a?(String)
422
+ JSON.parse(value)
405
423
  end
406
424
 
407
- private
425
+ def init_node
426
+ return if source.is_a?(Array)
427
+ @id = source.values_at('tag', 'id').compact.first&.upcase
428
+ @name = source['name']
429
+ @weight = source['weight']
430
+ @archived = as_boolean(source['archived'])
431
+ @archived_token = source['archived_token']
432
+ @classifications = into_a(source['classifications']).map do |value|
433
+ treat_classication(value)
434
+ end
435
+ @classification_names = into_a(source['classification_names'])
436
+ @raw_nodes = source['nodes'] || []
437
+ end
408
438
 
409
439
  def init_hashes
410
- @hash_tags = {}
440
+ @hash_tags = {}
411
441
  @hash_tags[@id] = self unless top?
412
- @hash_tags = nodes.reduce(@hash_tags) do |h,n|
442
+ @hash_tags = nodes.reduce(@hash_tags) do |h, n|
413
443
  h.merge(n.hash)
414
444
  end
415
- @hash_paths = {}
445
+ @hash_paths = {}
416
446
  @hash_paths[@id] = @path unless top?
417
- @hash_paths = nodes.reduce(@hash_paths) do |h,n|
447
+ @hash_paths = nodes.reduce(@hash_paths) do |h, n|
418
448
  h.merge(n.hash_paths)
419
449
  end
420
450
  end
451
+
452
+ ## Helpers
453
+ # Gathers the deepest nodes among `values`
454
+ # @note it will only return multiple nodes if they
455
+ # they are all at the same level
456
+ # @return [Array<Nodes, Integer>]
457
+ def get_deepest_nodes_among_tags(*values)
458
+ ddepth = -1
459
+ tnodes = []
460
+
461
+ values.each do |ttag|
462
+ msg = "Couldn't find the node of #{ttag} in the tag-tree definition"
463
+ raise msg unless (cnode = node(ttag))
464
+
465
+ next unless cnode.depth >= ddepth
466
+ next tnodes.push(cnode) if cnode.depth == ddepth
467
+
468
+ # cnode.depth > ddepth
469
+ tnodes = [cnode]
470
+ ddepth = cnode.depth
471
+ end
472
+
473
+ [tnodes, ddepth]
474
+ end
475
+
476
+ def treat_classication(value)
477
+ return value unless value.is_a?(String)
478
+ value.strip.gsub(/\W+/, '').downcase
479
+ end
480
+
481
+ # Helper to convert to array
482
+ def into_a(value)
483
+ if value.is_a?(String)
484
+ value.split('|')
485
+ else
486
+ [value].flatten
487
+ end.compact
488
+ end
489
+
490
+ def as_boolean(value)
491
+ return false if value.nil? || value == false
492
+ return true if value == true
493
+ return false if value.to_s.strip.empty?
494
+ return true if %w[yes x true].include?(value.downcase)
495
+ false
496
+ end
421
497
  end
422
498
  end
423
499
  end
@@ -5,6 +5,7 @@ module Eco
5
5
  end
6
6
  end
7
7
 
8
+ require_relative 'organization/node_classifications'
8
9
  require_relative 'organization/tag_tree'
9
10
  require_relative 'organization/presets_factory'
10
11
  require_relative 'organization/preferences'