eco-helpers 1.5.1 → 1.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -2
  3. data/LICENSE +21 -0
  4. data/eco-helpers.gemspec +1 -1
  5. data/lib/eco/api.rb +2 -0
  6. data/lib/eco/api/common.rb +4 -0
  7. data/lib/eco/api/common/base_loader.rb +54 -0
  8. data/lib/eco/api/common/class_auto_loader.rb +109 -0
  9. data/lib/eco/api/common/class_helpers.rb +33 -0
  10. data/lib/eco/api/common/class_hierarchy.rb +1 -1
  11. data/lib/eco/api/common/class_meta_basics.rb +16 -0
  12. data/lib/eco/api/common/loaders.rb +13 -0
  13. data/lib/eco/api/common/loaders/error_handler.rb +41 -0
  14. data/lib/eco/api/common/loaders/parser.rb +127 -0
  15. data/lib/eco/api/common/loaders/policy.rb +25 -0
  16. data/lib/eco/api/common/loaders/use_case.rb +40 -0
  17. data/lib/eco/api/common/people/default_parsers.rb +3 -12
  18. data/lib/eco/api/common/people/default_parsers/boolean_parser.rb +13 -23
  19. data/lib/eco/api/common/people/default_parsers/csv_parser.rb +20 -35
  20. data/lib/eco/api/common/people/default_parsers/date_parser.rb +15 -26
  21. data/lib/eco/api/common/people/default_parsers/freemium_parser.rb +15 -25
  22. data/lib/eco/api/common/people/default_parsers/login_providers_parser.rb +26 -0
  23. data/lib/eco/api/common/people/default_parsers/multi_parser.rb +15 -27
  24. data/lib/eco/api/common/people/default_parsers/numeric_parser.rb +14 -19
  25. data/lib/eco/api/common/people/default_parsers/policy_groups_parser.rb +24 -35
  26. data/lib/eco/api/common/people/default_parsers/send_invites_parser.rb +15 -25
  27. data/lib/eco/api/common/people/entries.rb +54 -24
  28. data/lib/eco/api/common/people/entry_factory.rb +10 -8
  29. data/lib/eco/api/common/people/person_attribute_parser.rb +29 -12
  30. data/lib/eco/api/common/people/person_entry.rb +308 -216
  31. data/lib/eco/api/common/people/person_entry_attribute_mapper.rb +3 -2
  32. data/lib/eco/api/common/people/person_parser.rb +51 -18
  33. data/lib/eco/api/common/session/logger.rb +4 -0
  34. data/lib/eco/api/common/version_patches/ecoportal_api/external_person.rb +2 -0
  35. data/lib/eco/api/common/version_patches/ecoportal_api/internal_person.rb +1 -1
  36. data/lib/eco/api/common/version_patches/exception.rb +22 -0
  37. data/lib/eco/api/custom.rb +13 -0
  38. data/lib/eco/api/custom/error_handler.rb +20 -0
  39. data/lib/eco/api/custom/namespace.rb +7 -0
  40. data/lib/eco/api/custom/parser.rb +50 -0
  41. data/lib/eco/api/custom/policy.rb +28 -0
  42. data/lib/eco/api/custom/use_case.rb +16 -0
  43. data/lib/eco/api/error.rb +1 -0
  44. data/lib/eco/api/error/handlers.rb +10 -3
  45. data/lib/eco/api/microcases.rb +17 -13
  46. data/lib/eco/api/microcases/account_excluded.rb +24 -0
  47. data/lib/eco/api/microcases/append_usergroups.rb +19 -0
  48. data/lib/eco/api/microcases/core_excluded.rb +4 -4
  49. data/lib/eco/api/microcases/{set_default_group.rb → fix_default_group.rb} +10 -9
  50. data/lib/eco/api/microcases/fix_filter_tags.rb +26 -6
  51. data/lib/eco/api/microcases/people_cache.rb +17 -0
  52. data/lib/eco/api/microcases/people_load.rb +59 -0
  53. data/lib/eco/api/microcases/people_refresh.rb +31 -0
  54. data/lib/eco/api/microcases/people_search.rb +65 -0
  55. data/lib/eco/api/microcases/refresh_abilities.rb +19 -0
  56. data/lib/eco/api/microcases/refresh_default_tag.rb +27 -0
  57. data/lib/eco/api/microcases/s3upload_targets.rb +39 -0
  58. data/lib/eco/api/microcases/set_account.rb +7 -19
  59. data/lib/eco/api/microcases/set_core.rb +5 -5
  60. data/lib/eco/api/microcases/set_core_with_supervisor.rb +23 -0
  61. data/lib/eco/api/microcases/set_supervisor.rb +17 -13
  62. data/lib/eco/api/microcases/strict_search.rb +12 -7
  63. data/lib/eco/api/microcases/with_each.rb +27 -0
  64. data/lib/eco/api/microcases/with_each_leaver.rb +24 -0
  65. data/lib/eco/api/microcases/with_each_present.rb +30 -0
  66. data/lib/eco/api/microcases/with_each_starter.rb +30 -0
  67. data/lib/eco/api/microcases/with_each_subordinate.rb +34 -0
  68. data/lib/eco/api/microcases/with_supervisor.rb +36 -0
  69. data/lib/eco/api/organization/people.rb +72 -35
  70. data/lib/eco/api/organization/presets_factory.rb +13 -4
  71. data/lib/eco/api/policies.rb +11 -7
  72. data/lib/eco/api/session.rb +54 -24
  73. data/lib/eco/api/session/batch.rb +1 -1
  74. data/lib/eco/api/session/batch/base_policy.rb +7 -6
  75. data/lib/eco/api/session/batch/errors.rb +28 -4
  76. data/lib/eco/api/session/batch/feedback.rb +7 -1
  77. data/lib/eco/api/session/batch/job.rb +40 -23
  78. data/lib/eco/api/session/batch/jobs.rb +9 -4
  79. data/lib/eco/api/session/batch/jobs_groups.rb +1 -1
  80. data/lib/eco/api/session/batch/request_stats.rb +91 -58
  81. data/lib/eco/api/session/batch/status.rb +35 -31
  82. data/lib/eco/api/session/config.rb +104 -42
  83. data/lib/eco/api/session/config/api.rb +17 -6
  84. data/lib/eco/api/session/config/logger.rb +2 -2
  85. data/lib/eco/api/session/config/post_launch.rb +1 -1
  86. data/lib/eco/api/session/config/workflow.rb +8 -7
  87. data/lib/eco/api/usecases.rb +47 -33
  88. data/lib/eco/api/usecases/backup/append_usergroups_case.rb +36 -0
  89. data/lib/eco/api/usecases/backup/create_case.rb +104 -0
  90. data/lib/eco/api/usecases/backup/create_details_case.rb +31 -0
  91. data/lib/eco/api/usecases/backup/create_details_with_supervisor_case.rb +48 -0
  92. data/lib/eco/api/usecases/backup/hris_case.rb +124 -0
  93. data/lib/eco/api/usecases/backup/set_default_tag_case.rb +49 -0
  94. data/lib/eco/api/usecases/backup/set_supervisor_case.rb +41 -0
  95. data/lib/eco/api/usecases/backup/transfer_account_case.rb +90 -0
  96. data/lib/eco/api/usecases/backup/update_case.rb +112 -0
  97. data/lib/eco/api/usecases/backup/update_details_case.rb +64 -0
  98. data/lib/eco/api/usecases/backup/upsert_case.rb +114 -0
  99. data/lib/eco/api/usecases/base_case.rb +2 -0
  100. data/lib/eco/api/usecases/base_io.rb +3 -3
  101. data/lib/eco/api/usecases/default_cases.rb +23 -53
  102. data/lib/eco/api/usecases/default_cases/append_usergroups_case.rb +10 -31
  103. data/lib/eco/api/usecases/default_cases/change_email_case.rb +23 -47
  104. data/lib/eco/api/usecases/default_cases/codes_to_tags_case.rb +56 -43
  105. data/lib/eco/api/usecases/default_cases/create_case.rb +15 -101
  106. data/lib/eco/api/usecases/default_cases/create_details_case.rb +11 -26
  107. data/lib/eco/api/usecases/default_cases/create_details_with_supervisor_case.rb +12 -43
  108. data/lib/eco/api/usecases/default_cases/delete_sync_case.rb +11 -0
  109. data/lib/eco/api/usecases/default_cases/delete_trans_case.rb +14 -0
  110. data/lib/eco/api/usecases/default_cases/email_as_id_case.rb +10 -21
  111. data/lib/eco/api/usecases/default_cases/hris_case.rb +23 -120
  112. data/lib/eco/api/usecases/default_cases/new_email_case.rb +10 -23
  113. data/lib/eco/api/usecases/default_cases/new_id_case.rb +11 -25
  114. data/lib/eco/api/usecases/default_cases/new_id_case0.rb +14 -0
  115. data/lib/eco/api/usecases/default_cases/org_data_convert_case.rb +83 -0
  116. data/lib/eco/api/usecases/default_cases/refresh_abilities_case.rb +30 -0
  117. data/lib/eco/api/usecases/default_cases/refresh_case.rb +7 -20
  118. data/lib/eco/api/usecases/default_cases/reinvite_sync_case.rb +11 -0
  119. data/lib/eco/api/usecases/default_cases/reinvite_trans_case.rb +17 -0
  120. data/lib/eco/api/usecases/default_cases/remove_account_sync_case.rb +11 -0
  121. data/lib/eco/api/usecases/default_cases/remove_account_trans_case.rb +17 -0
  122. data/lib/eco/api/usecases/default_cases/reset_landing_page_case.rb +9 -19
  123. data/lib/eco/api/usecases/default_cases/restore_db_case.rb +92 -0
  124. data/lib/eco/api/usecases/default_cases/set_default_tag_case.rb +32 -40
  125. data/lib/eco/api/usecases/default_cases/set_supervisor_case.rb +15 -33
  126. data/lib/eco/api/usecases/default_cases/switch_supervisor_case.rb +66 -57
  127. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +36 -44
  128. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +40 -55
  129. data/lib/eco/api/usecases/default_cases/transfer_account_case.rb +264 -84
  130. data/lib/eco/api/usecases/default_cases/update_case.rb +15 -109
  131. data/lib/eco/api/usecases/default_cases/update_details_case.rb +14 -61
  132. data/lib/eco/api/usecases/default_cases/upsert_case.rb +16 -111
  133. data/lib/eco/api/usecases/use_case_io.rb +9 -9
  134. data/lib/eco/cli/config.rb +10 -2
  135. data/lib/eco/cli/config/default.rb +2 -1
  136. data/lib/eco/cli/config/default/input_filters.rb +58 -0
  137. data/lib/eco/cli/config/default/options.rb +60 -25
  138. data/lib/eco/cli/config/default/people.rb +4 -4
  139. data/lib/eco/cli/config/default/people_filters.rb +108 -0
  140. data/lib/eco/cli/config/default/usecases.rb +69 -32
  141. data/lib/eco/cli/config/default/workflow.rb +37 -27
  142. data/lib/eco/cli/config/filters.rb +50 -0
  143. data/lib/eco/cli/config/filters/input_filters.rb +29 -0
  144. data/lib/eco/cli/config/filters/people_filters.rb +29 -0
  145. data/lib/eco/cli/config/help.rb +49 -0
  146. data/lib/eco/cli/config/options_set.rb +17 -1
  147. data/lib/eco/cli/config/use_cases.rb +79 -53
  148. data/lib/eco/cli/scripting.rb +10 -2
  149. data/lib/eco/cli/scripting/args_helpers.rb +25 -15
  150. data/lib/eco/cli/scripting/argument.rb +1 -0
  151. data/lib/eco/cli/scripting/arguments.rb +1 -1
  152. data/lib/eco/csv/table.rb +1 -1
  153. data/lib/eco/data/crypto/encryption.rb +3 -0
  154. data/lib/eco/language/match.rb +19 -9
  155. data/lib/eco/language/match_modifier.rb +13 -5
  156. data/lib/eco/language/models/collection.rb +77 -56
  157. data/lib/eco/language/models/parser_serializer.rb +39 -15
  158. data/lib/eco/version.rb +1 -1
  159. metadata +63 -18
  160. data/lib/eco/api/microcases/set_default_tag.rb +0 -23
  161. data/lib/eco/api/session/task.rb +0 -175
  162. data/lib/eco/api/usecases/default_case.rb +0 -19
  163. data/lib/eco/api/usecases/default_cases/delete_case.rb +0 -32
  164. data/lib/eco/api/usecases/default_cases/recover_db_case.rb +0 -99
  165. data/lib/eco/api/usecases/default_cases/refresh_presets_case.rb +0 -26
  166. data/lib/eco/api/usecases/default_cases/reinvite_case.rb +0 -41
  167. data/lib/eco/api/usecases/default_cases/remove_account_case.rb +0 -38
  168. data/lib/eco/api/usecases/microed_cases/hris_case.rb +0 -53
  169. data/lib/eco/api/usecases/microed_cases/update_case.rb +0 -33
  170. data/lib/eco/api/usecases/microed_cases/update_details_case.rb +0 -30
  171. data/lib/eco/api/usecases/microed_cases/upsert_case.rb +0 -36
  172. data/lib/eco/cli/config/default/filters.rb +0 -70
  173. data/lib/eco/cli/config/people_filters.rb +0 -38
@@ -1,33 +1,21 @@
1
- module Eco
2
- module API
3
- module Common
4
- module People
5
- class DefaultParsers
6
- class MultiParser < People::BaseParser
1
+ class Eco::API::Common::People::DefaultParsers::MultiParser < Eco::API::Common::Loaders::Parser
2
+ attribute :multiple
7
3
 
8
- def process
9
- @parsers.define_attribute(:multiple) do |parser|
10
- parser.def_parser do |value|
11
- next value if value.is_a?(Array)
12
- value = value.split("|") if value&.include?("|")
13
- into_a(value)
14
- end.def_serializer do |value|
15
- into_a(value).join("|")
16
- end
17
- end
18
- end
4
+ def parser(value, deps)
5
+ into_a(value)
6
+ end
19
7
 
20
- private
8
+ def serializer(value, deps)
9
+ into_a(value).join("|")
10
+ end
21
11
 
22
- def into_a(value)
23
- value = [] if value == nil
24
- value = [].push(value) unless value.is_a?(Array)
25
- value
26
- end
12
+ private
27
13
 
28
- end
29
- end
30
- end
31
- end
14
+ def into_a(value)
15
+ return [] if value.nil?
16
+ return value if value.is_a?(Array)
17
+ return value.split("|") if value.is_a?(String) && value.include?("|")
18
+ [].push(value)
32
19
  end
20
+
33
21
  end
@@ -1,23 +1,18 @@
1
- module Eco
2
- module API
3
- module Common
4
- module People
5
- class DefaultParsers
6
- class NumericParser < People::BaseParser
1
+ class Eco::API::Common::People::DefaultParsers::NumericParser < Eco::API::Common::Loaders::Parser
2
+ attribute :number
7
3
 
8
- def process
9
- @parsers.define_attribute(:number, dependencies: @options) do |parser|
10
- parser.def_parser do |value, deps|
11
- value = value.to_s.strip.empty? ? nil : (value.to_f rescue nil)
12
- end.def_serializer do |value|
13
- value.is_a?(Array) ? value.map { |v| v.to_s } : value.to_s
14
- end
15
- end
16
- end
4
+ def parser(value, deps)
5
+ value.is_a?(Array) ? value.map { |v| parse_number(v) } : parse_number(value)
6
+ end
17
7
 
18
- end
19
- end
20
- end
21
- end
8
+ def serializer(value, deps)
9
+ value.is_a?(Array) ? value.map { |v| v.to_s } : value.to_s
22
10
  end
11
+
12
+ private
13
+
14
+ def parse_number(value)
15
+ value.to_s.strip.empty? ? nil : (value.to_f rescue nil)
16
+ end
17
+
23
18
  end
@@ -1,42 +1,31 @@
1
- module Eco
2
- module API
3
- module Common
4
- module People
5
- class DefaultParsers
6
- class PolicyGroupsParser < People::BaseParser
1
+ class Eco::API::Common::People::DefaultParsers::PolicyGroupsParser < Eco::API::Common::Loaders::Parser
2
+ attribute "policy_group_ids"
7
3
 
8
- def process
9
- @parsers.define_attribute("policy_group_ids") do |parser|
10
- parser.def_parser do |columns_hash|
11
- policy_group_ids = []
12
- if policy_ids = columns_hash["policy_group_ids"]
13
- policy_group_ids = policy_ids.split("|").map do |name|
14
- policy_groups.to_id(name&.downcase.strip)
15
- end.compact
16
- end
17
- policy_group_ids.empty?? default_id : policy_group_ids.join("|")
18
- end.def_serializer do |person|
19
- ids = person&.account&.policy_group_ids || []
20
- ids.map do |id|
21
- policy_groups.to_name(id)
22
- end.compact.join("|")
23
- end
24
- end
25
- end
4
+ def parser(hash, deps)
5
+ policy_group_ids = []
6
+ if policy_ids = hash["policy_group_ids"]
7
+ policy_group_ids = policy_ids.split("|").map do |name|
8
+ policy_groups.to_id(name&.downcase.strip)
9
+ end.compact
10
+ end
11
+ policy_group_ids.empty?? default_id : policy_group_ids.join("|")
12
+ end
26
13
 
27
- private
14
+ def serializer(person, deps)
15
+ ids = person&.account&.policy_group_ids || []
16
+ ids.map do |id|
17
+ policy_groups.to_name(id)
18
+ end.compact.join("|")
19
+ end
28
20
 
29
- def policy_groups
30
- @policy_groups ||= ASSETS.config.policy_groups
31
- end
21
+ private
32
22
 
33
- def default_id
34
- @default_id ||= policy_groups.to_id(ASSETS.config.people.default_usergroup)
35
- end
23
+ def policy_groups
24
+ @policy_groups ||= ASSETS.config.policy_groups
25
+ end
36
26
 
37
- end
38
- end
39
- end
40
- end
27
+ def default_id
28
+ @default_id ||= policy_groups.to_id(ASSETS.config.people.default_usergroup)
41
29
  end
30
+
42
31
  end
@@ -1,30 +1,20 @@
1
- module Eco
2
- module API
3
- module Common
4
- module People
5
- class DefaultParsers
6
- class SendInvitesParser < People::BaseParser
1
+ class Eco::API::Common::People::DefaultParsers::SendInvitesParser < Eco::API::Common::Loaders::Parser
2
+ attribute "send_invites"
7
3
 
8
- def process
9
- @parsers.define_attribute("send_invites") do |parser|
10
- parser.def_parser do |hash|
11
- value = hash["send_invites"]
12
- value = value.first if value.is_a?(Array)
13
- truthy(value)
14
- end.def_serializer do |person|
15
- person.account && person.account.send_invites&.to_s
16
- end
17
- end
18
- end
4
+ def parser(hash, deps)
5
+ value = hash["send_invites"]
6
+ value = value.first if value.is_a?(Array)
7
+ truthy(value)
8
+ end
19
9
 
20
- private
10
+ def serializer(person, deps)
11
+ person.account && person.account.send_invites&.to_s
12
+ end
13
+
14
+ private
21
15
 
22
- def truthy (value)
23
- %w[true y yes x].include?(value.to_s&.downcase)
24
- end
25
- end
26
- end
27
- end
28
- end
16
+ def truthy (value)
17
+ %w[true y yes x].include?(value.to_s&.downcase)
29
18
  end
19
+
30
20
  end
@@ -3,7 +3,7 @@ module Eco
3
3
  module Common
4
4
  module People
5
5
  # Class meant to offer a _collection_ of entries, normally used to get parsed input data.
6
- # @attr_reader entries [Array<Eco::API::Common::PersonEntry] a pure `Array` object.
6
+ # @attr_reader entries [Array<Eco::API::Common::PeopleEntry] a pure `Array` object.
7
7
  class Entries < Eco::Language::Models::Collection
8
8
  # build the shortcuts of Collection
9
9
  attr_collection :id, :external_id, :email, :name, :supervisor_id
@@ -17,37 +17,41 @@ module Eco
17
17
  @caches_init = false
18
18
  end
19
19
 
20
- # Override `each` to make it work with supervisor hiearchy
21
- def each(params: {}, &block)
22
- return to_enum(:each) unless block
23
- @array_supers = sort_by_supervisors(@items) unless @caches_init
24
- @array_supers.each(&block)
20
+ # @!group Main identifier helpers
21
+
22
+ def id(*args)
23
+ attr('id', *args).first
24
+ end
25
+
26
+ def external_id(*args)
27
+ attr('external_id', *args).first
25
28
  end
26
29
 
27
30
  def [](id_or_ext)
28
31
  id(id_or_ext) || external_id(id_or_ext)
29
32
  end
33
+ # @!endgroup
30
34
 
31
- def id(*args)
32
- attr('id', *args).first
35
+ # @!group Special filters
36
+
37
+ def filter_tags_any(tags)
38
+ attr("filter_tags", tags, default_modifier.any.insensitive)
33
39
  end
34
40
 
35
- def external_id(*args)
36
- attr('external_id', *args).first
41
+ def filter_tags_all(tags)
42
+ attr("filter_tags", tags, default_modifier.all.insensitive)
37
43
  end
38
44
 
39
- # Helper to dump the entries into a CSV
40
- # @param filename [String] the destination file
41
- def export(filename)
42
- CSV.open(filename, "w") do |csv|
43
- entry = self.first
44
- header = entry.internal_entry.keys
45
- csv << header
46
- self.each do |entry|
47
- csv << entry.internal_entry.values
48
- end
49
- end
45
+ def policy_group_ids_any(ids)
46
+ attr("policy_group_ids", tags, default_modifier.any.insensitive)
47
+ end
48
+
49
+ def policy_group_ids_all(ids)
50
+ attr("policy_group_ids", tags, default_modifier.all.insensitive)
50
51
  end
52
+ # @!endgroup
53
+
54
+ # @!group Searchers
51
55
 
52
56
  # Search function to find an `entry` based on one of different options
53
57
  def entry(id: nil, external_id: nil, email: nil, strict: false)
@@ -68,11 +72,21 @@ module Eco
68
72
  # Search function to find an `entry` based on one of different options
69
73
  # see Eco::API::Common::People::Entries#entry
70
74
  def find(object, strict: false)
71
- id = object.respond_to?("id")? object.send("id") : nil
72
- external_id = object.respond_to?("external_id")? object.send("external_id") : nil
73
- email = object.respond_to?("email")? object.send("email") : nil
75
+ id = attr_value(object, "id")
76
+ external_id = attr_value(object, "external_id")
77
+ email = attr_value(object, "email")
74
78
  entry(id: id, external_id: external_id, email: email, strict: strict)
75
79
  end
80
+ # @!endgroup
81
+
82
+ # @!group Basic Collection Methods
83
+
84
+ # Override `each` to make it work with supervisor hiearchy
85
+ def each(&block)
86
+ return to_enum(:each) unless block
87
+ @array_supers = sort_by_supervisors(@items) unless @caches_init
88
+ @array_supers.each(&block)
89
+ end
76
90
 
77
91
  def exclude(object)
78
92
  exclude_people(into_a(object))
@@ -85,6 +99,21 @@ module Eco
85
99
  newFrom to_a - discarded
86
100
  end
87
101
 
102
+ # Helper to dump the entries into a CSV
103
+ # @param filename [String] the destination file
104
+ def export(filename)
105
+ CSV.open(filename, "w") do |csv|
106
+ entry = self.first
107
+ header = entry.internal_entry.keys
108
+ csv << header
109
+ self.each do |entry|
110
+ csv << entry.internal_entry.values
111
+ end
112
+ end
113
+ end
114
+
115
+ # @!group Groupping methods
116
+
88
117
  def email_id_maps
89
118
  email_present.group_by(:email).transform_values { |person| person.id }
90
119
  end
@@ -96,6 +125,7 @@ module Eco
96
125
  def to_h(attr = "id")
97
126
  super(attr || "id")
98
127
  end
128
+ # @!endgroup
99
129
 
100
130
  protected
101
131
 
@@ -12,7 +12,6 @@ module Eco
12
12
  # @param schema [Ecoportal::API::V1::PersonSchema] schema of person details that the parser will be based upon.
13
13
  # @param person_parser [nil, Eco::API::Common::People::PersonParser] set of attribute, type and format parsers/serializers.
14
14
  # @param attr_map [nil, Eco::Data::Mapper] attribute names mapper to translate external names into internal ones and _vice versa_.
15
- # @param logger [Common::Session::Logger, ::Logger] object to manage logs.
16
15
  def initialize(e, schema:, person_parser: nil, attr_map: nil)
17
16
  fatal "Constructor needs a PersonSchema. Given: #{schema}" if !schema.is_a?(Ecoportal::API::V1::PersonSchema)
18
17
  fatal "Expecting PersonParser. Given: #{person_parser}" if person_parser && !person_parser.is_a?(Eco::API::Common::People::PersonParser)
@@ -20,13 +19,12 @@ module Eco
20
19
  super(e)
21
20
 
22
21
  @schema = Ecoportal::API::V1::PersonSchema.new(JSON.parse(schema.doc.to_json))
23
-
24
22
  @source_person_parser = person_parser
25
23
 
26
- # load default parsers
27
- @person_parser = Eco::API::Common::People::DefaultParsers.new(schema: @schema)
28
- # merge the custom parsers
29
- @person_parser = @person_parser.merge(@source_person_parser)
24
+ # load default parser + custom parsers
25
+ base_parser = Eco::API::Common::People::DefaultParsers.new(schema: @schema).merge(@source_person_parser)
26
+ # new parser with linked schema
27
+ @person_parser = @source_person_parser.new(schema: @schema).merge(base_parser)
30
28
  @person_parser_patch_version = @source_person_parser.patch_version
31
29
 
32
30
  @attr_map = attr_map
@@ -79,8 +77,12 @@ module Eco
79
77
  arr_hash = []
80
78
  if Eco::API::Common::Session::FileManager.file_exists?(file)
81
79
  encoding ||= Eco::API::Common::Session::FileManager.encoding(file)
80
+ encoding = (encoding != "utf-8")? "#{encoding}|utf-8": encoding
82
81
  file_content = File.read(file, encoding: encoding)
83
- arr_hash = person_parser.parse(format, file_content)
82
+ arr_hash = person_parser.parse(format, file_content).map.each_with_index do |entry_hash, i|
83
+ j = (format == :csv)? i + 2 : i + 1
84
+ entry_hash.tap {|hash| hash["idx"] = j}
85
+ end
84
86
  else
85
87
  logger.warn("File does not exist: #{file}")
86
88
  end
@@ -119,7 +121,7 @@ module Eco
119
121
  deps = {"supervisor_id" => {people: data}}
120
122
 
121
123
  data_entries = data.map do |person|
122
- self.new(person, dependencies: deps).to_hash
124
+ self.new(person, dependencies: deps).external_entry
123
125
  end
124
126
 
125
127
  File.open(file, "w", enconding: encoding) do |fd|
@@ -12,21 +12,39 @@ module Eco
12
12
  # parser will be active/used.
13
13
  # - this is important to help avoiding to set values that are not present in the input entry.
14
14
  # - if you have doubts about using it or not, do not use it.
15
- # @param active_when [Proc] that expects a list of internal attributes or the internal entry itself
15
+ # @param phase [Symbol] the phase when this parser should be active.
16
+ # Must be one of [`:internal`, `:final`]
17
+ # @param active_when [Proc] that expects a list of internal attributes or the internal entry itself.
16
18
  # By default, an attribute paraser is active if in the entry to parse the internal attribute is present.
17
- def def_parser(active_when: nil, &block)
19
+ def def_parser(phase = :internal, active_when: nil, &block)
18
20
  @active_when = attribute_present(active_when)
19
- super(&block)
21
+ super(phase, &block)
20
22
  end
21
23
 
22
- # Determines if for a given source data to parse, this parser should be active or not
23
- # @return [Boolean] `true` if there's no callback defined or there is and evaluates `true`
24
- # when for the current data to parse. It returns `false` otherwise.
25
- def parser_active?(source_data)
26
- @active_when.call(source_data)
24
+ # @param phase [Symbol] the phase when this serializer should be active.
25
+ # Must be one of [`:person,` `:final`, `:internal`]
26
+ def def_serializer(phase = :person, &block)
27
+ super(phase, &block)
27
28
  end
28
29
 
29
- # Helper to build the `active_when` condition
30
+ # Determines if for a given source data to parse, this parser should be active or not.
31
+ # @param phase [Symbol] the phase when this parser should be active.
32
+ # Must be one of [`:internal`, `:final`]
33
+ # @return [Boolean] `true` if there's parser defined and `source_data` activates it.
34
+ def parser_active?(source_data, phase = :any)
35
+ (phase == :any || parser_category?(phase)) && @active_when.call(source_data)
36
+ end
37
+
38
+ # Determines a serializer should be active or not.
39
+ # @param phase [Symbol] the phase when this serializer should be active.
40
+ # Must be one of [`:person,` `:final`, `:internal`]
41
+ # @return [Boolean] `true` if there's serializer defined.
42
+ def serializer_active?(phase = :any)
43
+ (phase == :any) || serializer_category?(phase)
44
+ end
45
+
46
+
47
+ # Helper to build the `active_when` condition.
30
48
  def active_when_any?(*attrs)
31
49
  Proc.new do |source_data|
32
50
  keys = data_keys(source_data)
@@ -34,7 +52,7 @@ module Eco
34
52
  end
35
53
  end
36
54
 
37
- # Helper to build the `active_when` condition
55
+ # Helper to build the `active_when` condition.
38
56
  def active_when_all?(*attrs)
39
57
  Proc.new do |source_data|
40
58
  keys = data_keys(source_data)
@@ -44,7 +62,7 @@ module Eco
44
62
 
45
63
  private
46
64
 
47
- # by default, an attribute paraser is active if in the entry to parse
65
+ # By default, an attribute paraser is active if in the entry to parse
48
66
  # the internal attribute is present
49
67
  def attribute_present(active_when)
50
68
  Proc.new do |source_data|
@@ -67,7 +85,6 @@ module Eco
67
85
  end
68
86
  end
69
87
 
70
-
71
88
  end
72
89
  end
73
90
  end
@@ -9,7 +9,7 @@ module Eco
9
9
  # - if `data` is a `Person` object, its behaviour is `serialise`.
10
10
  # - if `data` is **not** a `Person` object, it does a `parse`.
11
11
  # - currently **in rework**, so there may be subtle differences that make it temporarily unstable (yet it is reliable).
12
- # @param data [Hash, Person] `Person` object to be serialized or hashed entry (`CSV::Row` is accepted).
12
+ # @param data [Hash, Ecoportal::API::V1::Person] `Person` object to be serialized or hashed entry (`CSV::Row` is accepted).
13
13
  # @param person_parser [Common::People::PersonParser] parser/serializer of person attributes (it contains a set of attribute parsers).
14
14
  # @param attr_map [Eco::Data::Mapper] mapper to translate attribute names from _external_ to _internal_ names and _vice versa_.
15
15
  # @param dependencies [Hash] hash where _keys_ are internal attribute names. It is mostly used to deliver final dependencies to attribute parsers/serializers.
@@ -26,17 +26,43 @@ module Eco
26
26
  @emap = PersonEntryAttributeMapper.new(@source, person_parser: @person_parser, attr_map: @attr_map, logger: @logger)
27
27
 
28
28
  if parsing?
29
- @external_entry = data
30
- @serialized_entry = _mapped_entry(@external_entry)
31
- @internal_entry = _internal_entry(@serialized_entry)
29
+ @external_entry = __external_entry(data)
30
+ @mapped_entry = __mapped_entry(@external_entry)
31
+ @internal_entry = __internal_entry(@mapped_entry)
32
+ @final_entry = __final_entry(@internal_entry)
32
33
  else # SERIALIZING
33
34
  @person = data
34
- @internal_entry = _internal_entry(@person)
35
- @serialized_entry = _mapped_entry(@internal_entry)
36
- #@external_entry = external_entry
35
+ @final_entry = __final_entry(@person)
36
+ @internal_entry = __internal_entry(@final_entry)
37
+ @mapped_entry = __mapped_entry(@internal_entry)
38
+ @external_entry = __external_entry(@mapped_entry)
37
39
  end
38
40
  end
39
41
 
42
+ # Generates a new entry
43
+ # @return [PersonEntry]
44
+ def new(data)
45
+ self.class.new(data, person_parser: @person_parser, attr_map: @attr_map, dependencies: @deps, logger: @logger)
46
+ end
47
+
48
+ # @note completely serialized entry.
49
+ # @return [Hash] entry `Hash` with **external** attribute names, and values and types thereof.
50
+ def external_entry
51
+ @external_entry
52
+ end
53
+
54
+ # @note just one step away from being completely parsed (only types parsing pending).
55
+ # @return [Hash] entry `Hash` with **internal** attribute names and values, but **external** types.
56
+ def internal_entry
57
+ @internal_entry
58
+ end
59
+
60
+ # @note values ready to be set to a person.
61
+ # @return [Hash] entry `Hash` with **internal** attribute names, values and types.
62
+ def final_entry
63
+ @final_entry
64
+ end
65
+
40
66
  # To know if currently the object is in parse or serialize mode.
41
67
  # @return [Boolean] returns `true` if we are **parsing**, `false` otherwise.
42
68
  def parsing?
@@ -49,73 +75,83 @@ module Eco
49
75
  !parsing?
50
76
  end
51
77
 
78
+ # @note `Eco::API::Common::People::EntryFactory#entries` adds this `idx`
79
+ # @return [Integer] the entry number in the input file
80
+ def idx
81
+ final_entry["idx"]
82
+ end
83
+
52
84
  # @return [String, nil] the _internal id_ of this person if defined.
53
85
  def id
54
- @internal_entry["id"]
86
+ final_entry["id"]
55
87
  end
56
88
 
57
89
  def id?
58
- @internal_entry.key?("id")
90
+ final_entry.key?("id")
59
91
  end
60
92
 
61
93
  # @return [String, nil] the _external id_ of this person if defined.
62
94
  def external_id
63
- @internal_entry["external_id"]
95
+ final_entry["external_id"]
64
96
  end
65
97
 
66
98
  def external_id?
67
- @internal_entry.key?("external_id")
99
+ final_entry.key?("external_id")
68
100
  end
69
101
 
70
102
  # @return [String, nil] the _name_ of this person if defined.
71
103
  def name
72
- @internal_entry["name"]
104
+ final_entry["name"]
73
105
  end
74
106
 
75
107
  def name?
76
- @internal_entry.key?("name")
108
+ final_entry.key?("name")
77
109
  end
78
110
 
79
111
  # @return [String, nil] the _email_ of this person if defined.
80
112
  def email
81
- @internal_entry["email"]
113
+ final_entry["email"]
82
114
  end
83
115
 
84
116
  def email?
85
- @internal_entry.key?("email")
117
+ final_entry.key?("email")
86
118
  end
87
119
 
88
120
  # @return [String, nil] the _supervisor id_ of this person if defined.
89
121
  def supervisor_id
90
- @internal_entry["supervisor_id"]
122
+ final_entry["supervisor_id"]
91
123
  end
92
124
 
93
125
  def supervisor_id=(value)
94
- @internal_entry["supervisor_id"] = value
126
+ final_entry["supervisor_id"] = value
95
127
  end
96
128
 
97
129
  def supervisor_id?
98
- @internal_entry.key?("supervisor_id")
130
+ final_entry.key?("supervisor_id")
99
131
  end
100
132
 
101
133
  def filter_tags
102
- @internal_entry["filter_tags"]
134
+ final_entry["filter_tags"] || []
103
135
  end
104
136
 
105
137
  def filter_tags?
106
- @internal_entry.key?("filter_tags")
138
+ final_entry.key?("filter_tags")
139
+ end
140
+
141
+ def policy_group_ids
142
+ final_entry["policy_group_ids"] || []
107
143
  end
108
144
 
109
145
  def policy_group_ids?
110
- @internal_entry.key?("policy_group_ids")
146
+ final_entry.key?("policy_group_ids")
111
147
  end
112
148
 
113
149
  def default_tag?
114
- @internal_entry.key?("default_tag")
150
+ final_entry.key?("default_tag")
115
151
  end
116
152
 
117
153
  def default_tag
118
- @internal_entry["default_tag"]
154
+ final_entry["default_tag"]
119
155
  end
120
156
 
121
157
  # Provides a reference of this person.
@@ -124,9 +160,10 @@ module Eco
124
160
  options = into_a(options)
125
161
  case
126
162
  when options.include?(:identify)
127
- "'#{name}' ('#{external_id}': '#{email}')"
163
+ str_id = id ? "id: '#{id}'; " : ""
164
+ "(row: #{idx}) '#{name}' (#{str_id}ext_id: '#{external_id}'; email: '#{email}')"
128
165
  else
129
- @internal_entry.each.map do |k, v|
166
+ final_entry.each.map do |k, v|
130
167
  "'#{k}': '#{v.to_json}'"
131
168
  end.join(" | ")
132
169
  end
@@ -135,257 +172,302 @@ module Eco
135
172
  # Setter to fill in all the `core` properties of the `Person` that are present in the `Entry`.
136
173
  # @note it only sets those core properties defined in the entry.
137
174
  # Meaning that if an core property is not present in the entry, this will not be set on the target person.
138
- # @param person [Person] the person we want to set the core values to.
175
+ # @param person [Ecoportal::API::V1::Person] the person we want to set the core values to.
139
176
  # @param exclude [String, Array<String>] core attributes that should not be set/changed to the person.
140
177
  def set_core(person, exclude: nil)
141
178
  scoped_attrs = @emap.core_attrs - into_a(exclude)
142
- @internal_entry.slice(*scoped_attrs).each do |attr, value|
143
- _set_to_core(person, attr, value)
144
- end
145
- end
146
-
147
- # Setter to fill in all the schema `details` fields of the `Person` that are present in the `Entry`.
148
- # @note it only sets those details properties defined in the entry.
149
- # Meaning that if an details property is not present in the entry, this will not be set on the target person.
150
- # @param person [Person] the person we want to set the schema fields' values to.
151
- # @param exclude [String, Array<String>] schema field attributes that should not be set/changed to the person.
152
- def set_details(person, exclude: nil)
153
- person.add_details(@person_parser.schema) if !person.details || !person.details.schema_id
154
- scoped_attrs = @emap.details_attrs - into_a(exclude)
155
- @internal_entry.slice(*scoped_attrs).each do |attr, value|
156
- _set_to_details(person, attr, value)
179
+ @final_entry.slice(*scoped_attrs).each do |attr, value|
180
+ set_part(person, attr, value)
157
181
  end
158
182
  end
159
183
 
160
184
  # Setter to fill in the `account` properties of the `Person` that are present in the `Entry`.
161
185
  # @note it only sets those account properties defined in the entry.
162
186
  # Meaning that if an account property is not present in the entry, this will not be set on the target person.
163
- # @param person [Person] the person we want to set the account values to.
187
+ # @param person [Ecoportal::API::Internal::Person] the person we want to set the account values to.
164
188
  # @param exclude [String, Array<String>] account properties that should not be set/changed to the person.
165
189
  def set_account(person, exclude: nil)
166
190
  person.account = {} if !person.account
167
191
  person.account.permissions_preset = nil unless person.account.permissions_preset = "custom"
168
192
  scoped_attrs = @emap.account_attrs - into_a(exclude)
169
- @internal_entry.slice(*scoped_attrs).each do |attr, value|
170
- _set_to_account(person, attr, value)
193
+ @final_entry.slice(*scoped_attrs).each do |attr, value|
194
+ set_part(person.account, attr, value)
171
195
  end
172
196
  end
173
197
 
174
- # Entry represented in a `Hash` with **external** attribute names and values thereof.
175
- # @note normally used to obtain a **serialized entry**.
176
- # @return [Hash] with **external** names and values.
177
- def to_hash
178
- external_entry
179
- end
180
-
181
- # Entry represented in a `Hash` with **external** attribute names and values thereof.
182
- # @note normally used to obtain a **serialized entry**.
183
- # @return [Hash] with **external** names and values.
184
- def external_entry
185
- @emap.all_attrs.each_with_object({}) do |attr, hash|
186
- unless hash.key?(ext_attr = @emap.to_external(attr))
187
- hash[ext_attr] = @serialized_entry[attr]
188
- end
198
+ # Setter to fill in all the schema `details` fields of the `Person` that are present in the `Entry`.
199
+ # @note it only sets those details properties defined in the entry.
200
+ # Meaning that if an details property is not present in the entry, this will not be set on the target person.
201
+ # @param person [Ecoportal::API::V1::Person] the person we want to set the schema fields' values to.
202
+ # @param exclude [String, Array<String>] schema field attributes that should not be set/changed to the person.
203
+ def set_details(person, exclude: nil)
204
+ person.add_details(@person_parser.schema) if !person.details || !person.details.schema_id
205
+ scoped_attrs = @emap.details_attrs - into_a(exclude)
206
+ @final_entry.slice(*scoped_attrs).each do |attr, value|
207
+ set_part(person.details, attr, value)
189
208
  end
190
209
  end
191
210
 
192
- def internal_entry
193
- @internal_entry
211
+ private
212
+
213
+ # @return [Hash] entry in raw: that with **external** names, values and types.
214
+ def __external_entry(data)
215
+ return data if parsing?
216
+ _external_serializing(data)
194
217
  end
195
218
 
196
- def doc
197
- return @person.doc if instance_variable_defined?(:@person) && @person
219
+ # @return [Hash] that with **internal** names but **external** values and types.
220
+ def __mapped_entry(data)
221
+ return _mapped_parsing(data) if parsing?
222
+ _mapped_serializing(data)
223
+ end
198
224
 
199
- core_attrs = @emap.core_attrs
200
- details_attrs = @emap.details_attrs
201
- account_attrs = @emap.account_attrs
225
+ # @return [Hash] that with **internal** names and values, but **external** values and types.
226
+ def __internal_entry(data)
227
+ return _internal_parsing(data) if parsing?
228
+ _internal_serializing(data)
229
+ end
202
230
 
203
- internal_entry.slice(*core_attrs).tap do |core_hash|
204
- unless details_attrs.empty?
205
- schema_id = @person_parser.schema.id
206
- details_fields = @person_parser.schema.doc["fields"].each_with_object([]) do |fld, flds|
207
- if details_attrs.include?(fld.alt_id)
208
- flds << fld.merge("value" => internal_entry[fld.alt_id]).slice("id", "alt_id", "type", "name", "shared", "multiple", "value")
209
- end
210
- end
211
- core_hash.merge!({
212
- "details" => {
213
- "schema_id" => schema_id,
214
- "fields" => details_fields
215
- }
216
- })
217
- end
231
+ # @return [Hash] that with **internal** names, values and types.
232
+ def __final_entry(data)
233
+ return _final_parsing(data) if parsing?
234
+ _final_serializing(data)
235
+ end
218
236
 
219
- unless account_attrs.empty?
220
- account_hash = internal_entry.slice(*account_attrs)
221
- core_hash.merge!({
222
- "account" => account_hash
223
- })
237
+ # Serializing helper that maps internal attributes to external attribute names
238
+ # @note **Serialize**: here we unaliase internal attribute names into external ones.
239
+ # @param mapped_entry [Hash] that with **internal** names but **external** values and types.
240
+ # @return [Hash] with **external** names, values and types.
241
+ def _external_serializing(mapped_entry)
242
+ target_attrs = @emap.all_attrs | @emap.aliased_attrs
243
+ rest_keys = mapped_entry.keys - target_attrs
244
+ target_attrs -= ["send_invites"]
245
+ external_entry = target_attrs.each_with_object({}) do |attr, hash|
246
+ unless hash.key?(ext_attr = @emap.to_external(attr))
247
+ hash[ext_attr] = mapped_entry[attr]
224
248
  end
225
249
  end
250
+ merge_missing_attrs(external_entry, mapped_entry.slice(*rest_keys))
226
251
  end
227
252
 
228
- private
229
-
230
- def _set_to_core(person, attr, value)
231
- value = value&.downcase if attr == "email"
232
- multiple = ["filter_tags"].include?(attr)
233
- if multiple
234
- value = @person_parser.parse(:multiple, value)
235
- value = value.map { |v| v&.upcase } if attr == "filter_tags"
236
- # preserve previous order
237
- current = into_a(person.send(attr))
238
- value = (current & value) + (value - current)
239
- else
240
- value = value&.strip
253
+ # Parsing helper that aliases attribute names (from internal to external names)
254
+ # @note **Parse**: here we aliase external attribute names into internal ones.
255
+ # @param external_entry [Hash] entry in raw, with **external** names and values.
256
+ # @return [Hash] entry with **internal** names, but still **external** values and types.
257
+ def _mapped_parsing(external_entry)
258
+ mapped_hash = @emap.aliased_attrs.each_with_object({}) do |attr, hash|
259
+ hash[attr] = external_entry[@emap.to_external(attr)]
241
260
  end
261
+ external_entry.slice(*@emap.direct_attrs).merge(mapped_hash)
262
+ end
242
263
 
243
- person.send("#{attr}=", value)
264
+ # Serializing helper that **serializes values** that have a parser/serializer defined.
265
+ # @note **Serializing**:
266
+ # 1. here we tranform internal into external **values**.
267
+ # 2. when running the serializers, it overrides existing keys.
268
+ # @param internal_entry [Hash] entry with **internal** names and values, but **external** types.
269
+ # @return [Hash] entry with **internal** names and **external** values and types.
270
+ def _mapped_serializing(internal_entry)
271
+ internal_entry.merge(_serialize_values(internal_entry, :internal))
244
272
  end
245
273
 
246
- def _set_to_account(person, attr, value)
247
- return if !person.account
248
- multiple = ["policy_group_ids", "login_provider_ids"].include?(attr)
249
- if multiple
250
- value = @person_parser.parse(:multiple, value)
251
- # preserve previous order
252
- current = into_a(person.account.send(attr))
253
- value = (current & value) + (value - current)
274
+ # Parsing helper that just **parses the values** that have a parser/serializer defined.
275
+ # @note this entry will still miss the type parsing (i.e. to `Array` if `multiple`)
276
+ # @param mapped_entry [Hash] the entry with the _internal attribute_ names but the _external values_.
277
+ # @return [Hash] the `internal entry` with the **internal** attributes names and values.
278
+ def _internal_parsing(mapped_entry)
279
+ mapped_entry.merge(_parse_values(mapped_entry, :internal))
280
+ end
281
+
282
+ # Serializing helper that just creates the _internal entry_ out of a _parsed entry_ (serializes the type).
283
+ # @param final_entry [Hash] the entry with all _internal_ (attributes, values and types)
284
+ # @return [Hash] the `internal entry` with the **internal** attributes names and values, but external types.
285
+ def _internal_serializing(final_entry)
286
+ final_entry = final_entry.merge(_serialize_values(final_entry, :final))
287
+ core_account = @person_parser.target_attrs_account + @person_parser.target_attrs_core
288
+ core_account_hash = core_account.reduce({}) do |hash, attr|
289
+ hash.merge(hash_attr(attr, _serialize_type(attr, final_entry[attr])))
290
+ end
291
+ details_hash = @person_parser.target_attrs_details.reduce({}) do |hash, attr|
292
+ hash.merge(hash_attr(attr, _serialize_type(attr, final_entry[attr], schema: @person_parser.schema)))
293
+ end
294
+ merging(core_account_hash, details_hash) do |internal_entry|
295
+ merge_missing_attrs(internal_entry, final_entry)
254
296
  end
255
-
256
- person.account.send("#{attr}=", value)
257
297
  end
258
298
 
259
- def _set_to_details(person, attr, value)
260
- return if !person.details
261
- unless field = person.details.get_field(attr)
262
- fatal("Field '#{attr}' does not exist in details of schema: '#{person.details.schema_id}'")
299
+ # Parsing helper where attributes with custom parsers are already parsed, but
300
+ # it finishes to parse the types (i.e. to `Array` if `multiple`)
301
+ # @param internal_entry [Hash] the entry with the **internal** _attribute_ names and values but the **external** types.
302
+ # @return [Hash] the `parsed entry` with the **internal** final attributes names, values and types.
303
+ def _final_parsing(internal_entry)
304
+ core_account = @emap.account_attrs + @emap.core_attrs
305
+ core_account_hash = internal_entry.slice(*core_account).each_with_object({}) do |(attr, value), hash|
306
+ hash[attr] = _parse_type(attr, value)
263
307
  end
264
- value = nil if value.to_s.empty?
265
- value = @person_parser.parse(:multiple, value) if field.multiple
266
308
 
267
- if @person_parser.defined?(field.type.to_sym)
268
- value = @person_parser.parse(field.type.to_sym, value, deps: {"attr" => attr})
309
+ details_hash = internal_entry.slice(*@emap.details_attrs).each_with_object({}) do |(attr, value), hash|
310
+ hash[attr] = _parse_type(attr, value, schema: @person_parser.schema)
269
311
  end
270
312
 
271
- person.details[attr] = value
272
- end
273
-
274
- def _get_from_core (person, attr)
275
- person.send(attr)
313
+ merging(core_account_hash, details_hash) do |final_entry|
314
+ final_entry = merge_missing_attrs(final_entry, internal_entry)
315
+ final_entry.merge(_parse_values(final_entry, :final))
316
+ end
276
317
  end
277
318
 
278
- def _get_from_account (person, attr)
279
- return nil if !person.account
280
- multiple = ["policy_group_ids", "filter_tags", "login_provider_ids"].include?(attr)
281
- value = person.account.send(attr)
282
- value = @person_parser.serialize(:multiple, value) if multiple
283
- value
319
+ # Serializing helper that just creates the _parsed entry_ out of a `Person` object.
320
+ # @note
321
+ # - when unnesting attributes, the overriding precedence for collisions is
322
+ # - `core` -> _overrides_ -> `account` -> _overrides_ -> `details`
323
+ # - to keep things consistent, the `internal entry` hash has keys in this order:
324
+ # - `core`, `account`, `details`.
325
+ # @param person [Ecoportal::API::V1::Person] the `Person` object to transform into a _parsed entry_.
326
+ # @return [Hash] the `parsed entry` with the **internal** attributes names and internal typed values.
327
+ def _final_serializing(person)
328
+ core_hash = @person_parser.target_attrs_core.reduce({}) do |hash, attr|
329
+ hash.merge(hash_attr(attr, get_part(person, attr)))
330
+ end
331
+ account_hash = @person_parser.target_attrs_account.reduce({}) do |hash, attr|
332
+ hash.merge(hash_attr(attr, get_part(person.account, attr)))
333
+ end
334
+ details_hash = @person_parser.target_attrs_details.reduce({}) do |hash, attr|
335
+ hash.merge(hash_attr(attr, get_part(person.details, attr)))
336
+ end
337
+ merging(core_hash, account_hash, details_hash) do |final_entry|
338
+ final_entry["Has account?"] = !!person.account
339
+ final_entry.merge(_serialize_values(person, :person))
340
+ end
284
341
  end
285
342
 
286
- def _get_from_details(person, attr)
287
- return nil if !person.details || !person&.details&.schema_id
288
- unless field = person.details.get_field(attr)
289
- fatal("Field '#{attr}' does not exist in details of schema: '#{person.details.schema_id}'")
343
+ # HELPERS
344
+ def _serialize_values(entry, phase = :person)
345
+ @person_parser.active_attrs(entry, phase, process: :serialize).each_with_object({}) do |attr, hash|
346
+ data = entry.is_a?(Hash)? entry.merge(hash) : entry
347
+ serial_attr = @person_parser.serialize(attr, data, phase, deps: @deps[attr] || {})
348
+ hash.merge!(hash_attr(attr, serial_attr))
290
349
  end
291
- value = person.details[attr]
292
- value = @person_parser.serialize(:date, value) if field.type == "date"
293
- value = @person_parser.serialize(:multiple, value) if field.multiple
294
- value
295
350
  end
296
351
 
297
- # MAPPED ENTRY (when and where applicable)
298
- # To obtain an entry with internal names but external values.
299
- # @param data [Hash] external or raw entry (when parsing) or internal or parsed entry (when serializing).
300
- # @return [Hash] entry with **internal names** and **external values**.
301
- def _mapped_entry(data)
302
- return _aliased_entry(data) if parsing?
303
- _serialized_entry(data)
352
+ def _parse_values(entry, phase = :internal)
353
+ @person_parser.active_attrs(entry, phase).each_with_object({}) do |attr, hash|
354
+ parsed_attr = @person_parser.parse(attr, entry.merge(hash), phase)
355
+ hash.merge!(hash_attr(attr, parsed_attr))
356
+ end
304
357
  end
305
358
 
306
- # Parsing helper that aliases attribute names (from internal to external names)
307
- # @note **Parse**: here we aliase internal attribute names into external ones.
308
- # @param ext_entry [Hash] entry in raw, with **external** names and values.
309
- # @return [Hash] entry with **internal names** and **external values**.
310
- def _aliased_entry(ext_entry)
311
- aliased_hash = @emap.aliased_attrs.map do |attr|
312
- [attr, ext_entry[@emap.to_external(attr)]]
313
- end.to_h
314
-
315
- ext_entry.slice(*@emap.direct_attrs).merge(aliased_hash)
359
+ # Transforms each **typed** value into its `String` version
360
+ def _serialize_type(attr, value, schema: nil)
361
+ case
362
+ when !!schema
363
+ unless field = schema[attr]
364
+ fatal("Field '#{attr}' does not exist in details of schema: '#{schema.name}'")
365
+ end
366
+ value = @person_parser.serialize(:multiple, value) if field.multiple
367
+ if @person_parser.defined?(field.type.to_sym)
368
+ value = @person_parser.serialize(field.type.to_sym, value, deps: {"attr" => attr})
369
+ end
370
+ value
371
+ when ["policy_group_ids", "filter_tags", "login_provider_ids", "starred_ids"].include?(attr)
372
+ @person_parser.serialize(:multiple, value)
373
+ when ["freemium", "accept_eula"].include?(attr)
374
+ @person_parser.serialize(:boolean, value)
375
+ when ["subordinates"].include?(attr)
376
+ @person_parser.serialize(:number, value)
377
+ else
378
+ value
379
+ end
316
380
  end
317
381
 
318
- def hash_attr(attr, value)
319
- return value if value.is_a?(Hash)
320
- { attr => value }
321
- end
382
+ # Transforms each `String` value into its **typed** version
383
+ def _parse_type(attr, value, schema: nil)
384
+ value = value.strip if value.is_a?(String)
385
+ value = nil if value.to_s.strip.empty?
386
+ case
387
+ when !!schema
388
+ unless field = schema[attr]
389
+ fatal("Field '#{attr}' does not exist in details of schema: '#{schema.name}'")
390
+ end
391
+ value = @person_parser.parse(:multiple, value) if field.multiple
322
392
 
323
- # Serializing helper that serializes values (from internal to external values).
324
- # @note **Serializing**:
325
- # 1. here we tranform internal into external **values**.
326
- # 2. when running the serializers, it overrides existing keys.
327
- # @param unserialized_entry [Hash] entry with **internal** names and values.
328
- # @return [Hash] entry with **internal names** and **external values**.
329
- def _serialized_entry(unserialized_entry)
330
- serial_attrs = @person_parser.defined_attrs.reduce({}) do |serial_hash, attr|
331
- deps = @deps[attr] || {}
332
- serial_attr = @person_parser.serialize(attr, @person, deps: deps)
333
- serial_hash.merge(hash_attr(attr, serial_attr))
334
- end
335
- unserialized_entry.merge(serial_attrs).tap do |hash|
336
- if hash.key?("filter_tags") && hash["filter_tags"].is_a?(Array)
337
- hash["filter_tags"] = @person_parser.serialize(:multiple, hash["filter_tags"])
393
+ if @person_parser.defined?(field.type.to_sym)
394
+ value = @person_parser.parse(field.type.to_sym, value, deps: {"attr" => attr})
338
395
  end
396
+ value
397
+ when attr == "email"
398
+ value = value.strip.downcase if value
399
+ value
400
+ when ["policy_group_ids", "filter_tags", "login_provider_ids", "starred_ids"].include?(attr)
401
+ value = @person_parser.parse(:multiple, value)
402
+ value = (attr == "filter_tags")? value.compact.map(&:upcase) : value
403
+ value
404
+ when ["freemium", "accept_eula"].include?(attr)
405
+ @person_parser.parse(:boolean, value)
406
+ when ["subordinates"].include?(attr)
407
+ @person_parser.parse(:number, value)
408
+ else
409
+ value
339
410
  end
340
411
  end
341
412
 
342
- # To obtain an entry with internal names but external values.
343
- # @param data [Hash, Ecoportal::API::V1::Person] alised_entry (when parsing) or person (when serializing).
344
- # @return [Hash] the `internal entry` with the **internal** attributes names and values.
345
- def _internal_entry(data)
346
- return _parsed_entry(data) if parsing?
347
- _unserialized_entry(data)
413
+ # Merges multiple hashes giving overriding perference to the first ones.
414
+ # @return [Hash] with well sorted keys, as they came in the order of the input hashes.
415
+ def merging(*hashes)
416
+ sorted_keys = hashes.map {|h| h.keys}.flatten.uniq
417
+ rev_hash = hashes.reverse.each_with_object({}) {|h, out| out.merge!(h)}
418
+ merged = sorted_keys.each_with_object({}) do |k, h|
419
+ h[k] = rev_hash[k]
420
+ end
421
+ merged = yield(merged) if block_given?
422
+ merged
348
423
  end
349
424
 
350
- # Parsing helper that just **parses the values** that have a parser/serializer defined.
351
- # @param aliased_entry [Hash] the entry with the _internal attribute_ names but the _external values_.
352
- # @return [Hash] the `internal entry` with the **internal** attributes names and values.
353
- def _parsed_entry(aliased_entry)
354
- parsed = @person_parser.active_attrs(aliased_entry).each_with_object({}) do |attr, hash|
355
- hash[attr] = @person_parser.parse(attr, aliased_entry.merge(hash))
356
- end
357
- aliased_entry.merge(parsed)
425
+ # Adds to `dest_entry` the `keys` it misses from `source_entry`
426
+ def merge_missing_attrs(dest_entry, source_entry)
427
+ keys_rest = source_entry.keys - dest_entry.keys
428
+ dest_entry.merge(source_entry.slice(*keys_rest))
358
429
  end
359
430
 
360
- # Serializing helper that just creates the _internal entry_ out of a `Person` object.
361
- # @note
362
- # - when unnesting attributes, the overriding precedence for collisions is
363
- # - `core` -> _overrides_ -> `account` -> _overrides_ -> `details`
364
- # - to keep things consistent, the `internal entry` hash has keys in this order:
365
- # - `core`, `account`, `details`.
366
- # @param person [Ecoportal::API::V1::Person] the `Person` object to transform into an _internal entry_.
367
- # @return [Hash] the `internal entry` with the **internal** attributes names and values.
368
- def _unserialized_entry(person)
369
- core_hash = @person_parser.target_attrs_core.reduce({}) do |hash, attr|
370
- value = _get_from_core(person, attr)
371
- hash.merge(hash_attr(attr, value))
372
- end
431
+ def into_a(value)
432
+ value = [] if value == nil
433
+ value = [].push(value) unless value.is_a?(Array)
434
+ value
435
+ end
373
436
 
374
- details_hash = @person_parser.target_attrs_details.reduce({}) do |hash, attr|
375
- value = _get_from_details(person, attr)
376
- hash.merge(hash_attr(attr, value))
437
+ def get_part(obj, attr)
438
+ return unless obj
439
+ case obj
440
+ when Ecoportal::API::V1::PersonDetails
441
+ #unless field = obj.get_field(attr)
442
+ # fatal("Field '#{attr}' does not exist in details of schema: '#{obj.schema_id}'")
443
+ #end
444
+ obj[attr]
445
+ else
446
+ obj.send(attr)
377
447
  end
448
+ end
378
449
 
379
- account_hash = @person_parser.target_attrs_account.reduce({}) do |hash, attr|
380
- value = _get_from_account(person, attr)
381
- hash.merge(hash_attr(attr, value))
450
+ def set_part(obj, attr, value)
451
+ return unless obj
452
+ begin
453
+ case obj
454
+ when Ecoportal::API::V1::PersonDetails
455
+ unless field = obj.get_field(attr)
456
+ fatal("Field '#{attr}' does not exist in details of schema: '#{obj.schema_id}'")
457
+ end
458
+ obj[attr] = value
459
+ else
460
+ obj.send("#{attr}=", value)
461
+ end
462
+ rescue Exception => e
463
+ raise e.append_message " -- Entry #{to_s(:identify)}"
382
464
  end
465
+ end
383
466
 
384
- # merge by core overriding account and details
385
- rh = details_hash.merge(account_hash).merge(core_hash)
386
- # resort hash keys
387
- sorted_keys = core_hash.keys | account_hash.keys | details_hash.keys
388
- sorted_keys.reduce({}) {|h,k| h[k] = rh[k]; h}
467
+ # @return [Hash] `value` if it was a `Hash`, and `{ attr => value}` otherwise
468
+ def hash_attr(attr, value)
469
+ return value if value.is_a?(Hash)
470
+ { attr => value }
389
471
  end
390
472
 
391
473
  # LOGGER
@@ -398,11 +480,21 @@ module Eco
398
480
  raise msg
399
481
  end
400
482
 
401
- # HELPERS
402
- def into_a(value)
403
- value = [] if value == nil
404
- value = [].push(value) unless value.is_a?(Array)
405
- value
483
+ # Function to debug faste
484
+ def print_models
485
+ print_it = Proc.new do |name, model|
486
+ puts "#{name}:"
487
+ pp model
488
+ puts "*" * 30
489
+ end
490
+
491
+ fin = Proc.new {|x_| print_it.call("final_entry", @final_entry) }
492
+ int = Proc.new {|x_| print_it.call("internal_entry", @internal_entry) }
493
+ mad = Proc.new {|x_| print_it.call("mapped_entry", @mapped_entry) }
494
+ ext = Proc.new {|x_| print_it.call("external_entry", @external_entry) }
495
+
496
+ call_order = parsing? ? [ext, mad, int, fin] : [fin, int, mad, ext]
497
+ call_order.each {|proc| proc.call}
406
498
  end
407
499
 
408
500
  end