eco-helpers 1.4.1 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +145 -3
  3. data/LICENSE +21 -0
  4. data/eco-helpers.gemspec +11 -10
  5. data/lib/eco/api.rb +3 -0
  6. data/lib/eco/api/common.rb +5 -1
  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 +5 -10
  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 +20 -0
  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 +31 -0
  26. data/lib/eco/api/common/people/default_parsers/send_invites_parser.rb +15 -24
  27. data/lib/eco/api/common/people/entries.rb +54 -24
  28. data/lib/eco/api/common/people/entry_factory.rb +18 -15
  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 +5 -2
  32. data/lib/eco/api/common/people/person_parser.rb +52 -19
  33. data/lib/eco/api/common/session/base_session.rb +3 -6
  34. data/lib/eco/api/common/session/environment.rb +2 -23
  35. data/lib/eco/api/common/session/logger.rb +4 -0
  36. data/lib/eco/api/common/version_patches/ecoportal_api/external_person.rb +2 -0
  37. data/lib/eco/api/common/version_patches/ecoportal_api/internal_person.rb +9 -1
  38. data/lib/eco/api/common/version_patches/exception.rb +24 -2
  39. data/lib/eco/api/custom.rb +13 -0
  40. data/lib/eco/api/custom/error_handler.rb +20 -0
  41. data/lib/eco/api/custom/namespace.rb +7 -0
  42. data/lib/eco/api/custom/parser.rb +50 -0
  43. data/lib/eco/api/custom/policy.rb +28 -0
  44. data/lib/eco/api/custom/use_case.rb +16 -0
  45. data/lib/eco/api/error.rb +1 -0
  46. data/lib/eco/api/error/handlers.rb +10 -3
  47. data/lib/eco/api/microcases.rb +35 -0
  48. data/lib/eco/api/microcases/account_excluded.rb +24 -0
  49. data/lib/eco/api/microcases/append_usergroups.rb +19 -0
  50. data/lib/eco/api/microcases/core_excluded.rb +20 -0
  51. data/lib/eco/api/microcases/fix_default_group.rb +34 -0
  52. data/lib/eco/api/microcases/fix_filter_tags.rb +42 -0
  53. data/lib/eco/api/microcases/people_cache.rb +17 -0
  54. data/lib/eco/api/microcases/people_load.rb +59 -0
  55. data/lib/eco/api/microcases/people_refresh.rb +31 -0
  56. data/lib/eco/api/microcases/people_search.rb +65 -0
  57. data/lib/eco/api/microcases/refresh_abilities.rb +19 -0
  58. data/lib/eco/api/microcases/refresh_default_tag.rb +27 -0
  59. data/lib/eco/api/microcases/s3upload_targets.rb +39 -0
  60. data/lib/eco/api/microcases/set_account.rb +20 -0
  61. data/lib/eco/api/microcases/set_core.rb +18 -0
  62. data/lib/eco/api/microcases/set_core_with_supervisor.rb +23 -0
  63. data/lib/eco/api/microcases/set_supervisor.rb +30 -0
  64. data/lib/eco/api/microcases/strict_search.rb +19 -0
  65. data/lib/eco/api/microcases/with_each.rb +27 -0
  66. data/lib/eco/api/microcases/with_each_leaver.rb +24 -0
  67. data/lib/eco/api/microcases/with_each_present.rb +30 -0
  68. data/lib/eco/api/microcases/with_each_starter.rb +30 -0
  69. data/lib/eco/api/microcases/with_each_subordinate.rb +34 -0
  70. data/lib/eco/api/microcases/with_supervisor.rb +36 -0
  71. data/lib/eco/api/organization/people.rb +72 -35
  72. data/lib/eco/api/organization/presets_factory.rb +13 -4
  73. data/lib/eco/api/organization/presets_reference.json +9 -1
  74. data/lib/eco/api/organization/presets_values.json +4 -1
  75. data/lib/eco/api/policies.rb +11 -7
  76. data/lib/eco/api/session.rb +62 -29
  77. data/lib/eco/api/session/batch.rb +2 -45
  78. data/lib/eco/api/session/batch/base_policy.rb +7 -6
  79. data/lib/eco/api/session/batch/errors.rb +28 -4
  80. data/lib/eco/api/session/batch/feedback.rb +7 -1
  81. data/lib/eco/api/session/batch/job.rb +40 -23
  82. data/lib/eco/api/session/batch/jobs.rb +9 -4
  83. data/lib/eco/api/session/batch/jobs_groups.rb +1 -1
  84. data/lib/eco/api/session/batch/request_stats.rb +95 -58
  85. data/lib/eco/api/session/batch/status.rb +35 -31
  86. data/lib/eco/api/session/config.rb +106 -44
  87. data/lib/eco/api/session/config/api.rb +132 -7
  88. data/lib/eco/api/session/config/apis.rb +24 -25
  89. data/lib/eco/api/session/config/logger.rb +2 -2
  90. data/lib/eco/api/session/config/post_launch.rb +1 -1
  91. data/lib/eco/api/session/config/workflow.rb +8 -7
  92. data/lib/eco/api/usecases.rb +47 -33
  93. data/lib/eco/api/usecases/backup/append_usergroups_case.rb +36 -0
  94. data/lib/eco/api/usecases/backup/create_case.rb +104 -0
  95. data/lib/eco/api/usecases/backup/create_details_case.rb +31 -0
  96. data/lib/eco/api/usecases/backup/create_details_with_supervisor_case.rb +48 -0
  97. data/lib/eco/api/usecases/backup/hris_case.rb +124 -0
  98. data/lib/eco/api/usecases/backup/set_default_tag_case.rb +49 -0
  99. data/lib/eco/api/usecases/backup/set_supervisor_case.rb +41 -0
  100. data/lib/eco/api/usecases/backup/transfer_account_case.rb +90 -0
  101. data/lib/eco/api/usecases/backup/update_case.rb +112 -0
  102. data/lib/eco/api/usecases/backup/update_details_case.rb +64 -0
  103. data/lib/eco/api/usecases/backup/upsert_case.rb +114 -0
  104. data/lib/eco/api/usecases/base_case.rb +2 -0
  105. data/lib/eco/api/usecases/base_io.rb +3 -3
  106. data/lib/eco/api/usecases/default_cases.rb +23 -53
  107. data/lib/eco/api/usecases/default_cases/append_usergroups_case.rb +10 -31
  108. data/lib/eco/api/usecases/default_cases/change_email_case.rb +23 -47
  109. data/lib/eco/api/usecases/default_cases/codes_to_tags_case.rb +56 -43
  110. data/lib/eco/api/usecases/default_cases/create_case.rb +15 -101
  111. data/lib/eco/api/usecases/default_cases/create_details_case.rb +11 -26
  112. data/lib/eco/api/usecases/default_cases/create_details_with_supervisor_case.rb +12 -43
  113. data/lib/eco/api/usecases/default_cases/delete_sync_case.rb +11 -0
  114. data/lib/eco/api/usecases/default_cases/delete_trans_case.rb +14 -0
  115. data/lib/eco/api/usecases/default_cases/email_as_id_case.rb +10 -21
  116. data/lib/eco/api/usecases/default_cases/hris_case.rb +23 -120
  117. data/lib/eco/api/usecases/default_cases/new_email_case.rb +10 -23
  118. data/lib/eco/api/usecases/default_cases/new_id_case.rb +11 -25
  119. data/lib/eco/api/usecases/default_cases/new_id_case0.rb +14 -0
  120. data/lib/eco/api/usecases/default_cases/org_data_convert_case.rb +83 -0
  121. data/lib/eco/api/usecases/default_cases/refresh_abilities_case.rb +30 -0
  122. data/lib/eco/api/usecases/default_cases/refresh_case.rb +7 -20
  123. data/lib/eco/api/usecases/default_cases/reinvite_sync_case.rb +11 -0
  124. data/lib/eco/api/usecases/default_cases/reinvite_trans_case.rb +17 -0
  125. data/lib/eco/api/usecases/default_cases/remove_account_sync_case.rb +11 -0
  126. data/lib/eco/api/usecases/default_cases/remove_account_trans_case.rb +17 -0
  127. data/lib/eco/api/usecases/default_cases/reset_landing_page_case.rb +9 -19
  128. data/lib/eco/api/usecases/default_cases/restore_db_case.rb +92 -0
  129. data/lib/eco/api/usecases/default_cases/set_default_tag_case.rb +32 -40
  130. data/lib/eco/api/usecases/default_cases/set_supervisor_case.rb +15 -33
  131. data/lib/eco/api/usecases/default_cases/switch_supervisor_case.rb +66 -57
  132. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +36 -44
  133. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +40 -55
  134. data/lib/eco/api/usecases/default_cases/transfer_account_case.rb +264 -84
  135. data/lib/eco/api/usecases/default_cases/update_case.rb +15 -109
  136. data/lib/eco/api/usecases/default_cases/update_details_case.rb +14 -61
  137. data/lib/eco/api/usecases/default_cases/upsert_case.rb +16 -111
  138. data/lib/eco/api/usecases/use_case_io.rb +9 -9
  139. data/lib/eco/cli/config.rb +10 -2
  140. data/lib/eco/cli/config/default.rb +2 -1
  141. data/lib/eco/cli/config/default/input_filters.rb +58 -0
  142. data/lib/eco/cli/config/default/options.rb +60 -25
  143. data/lib/eco/cli/config/default/people.rb +4 -4
  144. data/lib/eco/cli/config/default/people_filters.rb +108 -0
  145. data/lib/eco/cli/config/default/usecases.rb +69 -32
  146. data/lib/eco/cli/config/default/workflow.rb +37 -27
  147. data/lib/eco/cli/config/filters.rb +50 -0
  148. data/lib/eco/cli/config/filters/input_filters.rb +29 -0
  149. data/lib/eco/cli/config/filters/people_filters.rb +29 -0
  150. data/lib/eco/cli/config/help.rb +49 -0
  151. data/lib/eco/cli/config/options_set.rb +17 -1
  152. data/lib/eco/cli/config/use_cases.rb +79 -53
  153. data/lib/eco/cli/scripting.rb +10 -2
  154. data/lib/eco/cli/scripting/args_helpers.rb +25 -15
  155. data/lib/eco/cli/scripting/argument.rb +1 -0
  156. data/lib/eco/cli/scripting/arguments.rb +1 -1
  157. data/lib/eco/csv.rb +8 -3
  158. data/lib/eco/csv/table.rb +1 -1
  159. data/lib/eco/data/crypto/encryption.rb +3 -0
  160. data/lib/eco/data/files/helpers.rb +6 -1
  161. data/lib/eco/language/match.rb +19 -9
  162. data/lib/eco/language/match_modifier.rb +13 -5
  163. data/lib/eco/language/models/collection.rb +77 -56
  164. data/lib/eco/language/models/parser_serializer.rb +39 -15
  165. data/lib/eco/version.rb +1 -1
  166. metadata +149 -63
  167. data/lib/eco/api/session/task.rb +0 -175
  168. data/lib/eco/api/usecases/default_case.rb +0 -19
  169. data/lib/eco/api/usecases/default_cases/delete_case.rb +0 -32
  170. data/lib/eco/api/usecases/default_cases/recover_db_case.rb +0 -98
  171. data/lib/eco/api/usecases/default_cases/refresh_presets_case.rb +0 -26
  172. data/lib/eco/api/usecases/default_cases/reinvite_case.rb +0 -41
  173. data/lib/eco/api/usecases/default_cases/remove_account_case.rb +0 -38
  174. data/lib/eco/cli/config/default/filters.rb +0 -70
  175. data/lib/eco/cli/config/people_filters.rb +0 -38
@@ -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