eco-helpers 2.0.25 → 2.0.26
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +53 -6
- data/lib/eco/api/common.rb +0 -1
- data/lib/eco/api/common/loaders.rb +2 -0
- data/lib/eco/api/common/loaders/base.rb +58 -0
- data/lib/eco/api/common/loaders/case_base.rb +33 -0
- data/lib/eco/api/common/loaders/error_handler.rb +2 -2
- data/lib/eco/api/common/loaders/parser.rb +30 -5
- data/lib/eco/api/common/loaders/policy.rb +1 -1
- data/lib/eco/api/common/loaders/use_case.rb +1 -1
- data/lib/eco/api/common/people/default_parsers/csv_parser.rb +93 -1
- data/lib/eco/api/common/people/entries.rb +83 -14
- data/lib/eco/api/common/people/entry_factory.rb +10 -9
- data/lib/eco/api/common/people/person_attribute_parser.rb +8 -0
- data/lib/eco/api/common/people/person_factory.rb +4 -2
- data/lib/eco/api/common/people/person_parser.rb +7 -1
- data/lib/eco/api/common/people/supervisor_helpers.rb +1 -1
- data/lib/eco/api/common/version_patches/ecoportal_api/external_person.rb +0 -8
- data/lib/eco/api/common/version_patches/ecoportal_api/internal_person.rb +0 -8
- data/lib/eco/api/microcases/set_core_with_supervisor.rb +4 -2
- data/lib/eco/api/microcases/set_supervisor.rb +29 -8
- data/lib/eco/api/microcases/with_each.rb +7 -3
- data/lib/eco/api/microcases/with_each_starter.rb +3 -2
- data/lib/eco/api/organization/people.rb +1 -1
- data/lib/eco/api/session.rb +7 -2
- data/lib/eco/api/session/batch/job.rb +8 -0
- data/lib/eco/api/usecases/default_cases/create_case.rb +10 -1
- data/lib/eco/api/usecases/default_cases/create_details_case.rb +10 -1
- data/lib/eco/api/usecases/default_cases/create_details_with_supervisor_case.rb +10 -1
- data/lib/eco/api/usecases/default_cases/hris_case.rb +6 -2
- data/lib/eco/api/usecases/default_cases/upsert_case.rb +10 -1
- data/lib/eco/cli/config/default/input.rb +2 -2
- data/lib/eco/cli/config/default/options.rb +23 -7
- data/lib/eco/cli/config/default/usecases.rb +16 -0
- data/lib/eco/cli/config/default/workflow.rb +7 -4
- data/lib/eco/cli/config/filters.rb +6 -2
- data/lib/eco/cli/config/filters/input_filters.rb +3 -2
- data/lib/eco/cli/config/filters/people_filters.rb +3 -2
- data/lib/eco/cli/config/help.rb +1 -1
- data/lib/eco/cli/config/options_set.rb +6 -4
- data/lib/eco/cli/config/use_cases.rb +6 -3
- data/lib/eco/csv.rb +2 -0
- data/lib/eco/version.rb +1 -1
- metadata +3 -2
- data/lib/eco/api/common/base_loader.rb +0 -72
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 722e1dc695f9d9fae5fbceca8a2269f4836393ee4dc50fc0f850e43fefc88789
|
4
|
+
data.tar.gz: bc34444cdf33bff51895fa5ed6bb7a89b89074e5bd11852c0a1a4f4bf6bcc574
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a18aaa7abf012872f21209907909f1c309ddb530586bc9e27d595ace57377c3c4dba2b0a7bacc902e160cf2f51d4171a5de3e0536b6eda4dff7d018307744ca
|
7
|
+
data.tar.gz: efcaa1832677c0cf13d5dfb0ba6a3abaa8bff3fec4e1d2639b2b832358a1e51b849e594da64827bed096eb4f8f03a5664a6a8245c58128acf3478eaf0e94d38a
|
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,62 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
-
## [2.0.
|
4
|
+
## [2.0.26] - 2021-06-xx
|
5
|
+
|
6
|
+
### Added
|
7
|
+
- `Eco::API::MicroCases#set_supervisor`, tries to keep in sync the `#subordinates` **count** of previous and new supervisor
|
8
|
+
- new **option** `-run-postlaunch` to run post launch cases, even when we run in `dry-run` mode
|
9
|
+
* when in `dry-run` it will **not** reload the people base of the session
|
10
|
+
- new **option** to append **new** entries to the `People` object
|
11
|
+
* **invokable** on **cli** via `-append-starters` (`{people: {append_created: true}}`)
|
12
|
+
* the following use cases include this option:
|
13
|
+
* `Eco::API::UseCases::DefaultCases::UpsertCase`
|
14
|
+
* `Eco::API::UseCases::DefaultCases::HrisCase`
|
15
|
+
* `Eco::API::UseCases::DefaultCases::CreateDetailsWithSupervisorCase`
|
16
|
+
* `Eco::API::UseCases::DefaultCases::CreateDetailsCase`
|
17
|
+
* `Eco::API::UseCases::DefaultCases::CreateCase`
|
18
|
+
* the option involves a new keyed argument `:append_created` in a couple of `MicroCases`
|
19
|
+
* `Eco::API::MicroCases#with_each`: where internally the search is performed against a copy of the `People` object.
|
20
|
+
* `Eco::API::MicroCases#with_each_starter`
|
21
|
+
* when `--help` is invoked option to filter the shown `-options`, `-usecases` and `filters` by a word contained in the option.
|
22
|
+
- **added** `csv` **header checks** for feed files, which entailed some changes:
|
23
|
+
* `Eco::API::Common::Loaders::Parser`
|
24
|
+
- new **subclass** `RequiredAttrs`, creatred when calling `.active_when_all` and `.active_when_any`
|
25
|
+
- **dependency** injection via `.dependencies` as `{required_attrs: RequiredAttrs}`
|
26
|
+
* **added** `Eco::API::Common::People::PersonParser#required_attrs` to offer all the `RequiredAttrs`, where defined
|
27
|
+
- the **new method** `#required_attrs` to expose the injected `RequiredAttrs`
|
28
|
+
* **new** keyed argument `check_headers:` in `Eco::API::Common::People::EntryFactory#entries`
|
29
|
+
- subsequent changes to accommodate the new param in `Eco::API::Session#csv_entries`
|
30
|
+
- `eco/cli/config/default/input` calls using this param to `true`
|
31
|
+
* `Eco::API::Common::People::DefaultParsers::CSVParser`
|
32
|
+
- added option `check_headers` via `dependencies` that enables the headers check
|
33
|
+
- it will now offer detailed warning messages on what can happen with the **missing headers**
|
34
|
+
* it will also list the **unknown header** names
|
35
|
+
|
36
|
+
### Changed
|
37
|
+
- `Eco::API::MicroCases#set_supervisor`, the order of the 2 first parameters
|
38
|
+
- `Eco::API::Organization::People`: internally `@by_id` cache Hash included `nil` values => **not** any more.
|
39
|
+
- removed **unused** methods on **patches** for `Ecoportal::API::V1::Person` and `Ecoportal::API::Internal::Person`
|
40
|
+
* specifically `#reset_account!` and `#consolidate_account!` as well as `#reset_details!` and `#consolidate_details!`
|
41
|
+
- internal changes in `Eco::API::Common::People::Entries#entry`
|
42
|
+
* **added** option to trigger `MultipleSearchResults` StandardError when multiple candiates are found.
|
43
|
+
* **removed** `nil` values from the `caches` (the Hashes to optimize the search)
|
44
|
+
- slight structure refactor of `Eco::API::Common::Loaders`
|
45
|
+
* moved base class to subfolder/namespace
|
46
|
+
* decoupled pure `Loader` logics to `Loaders::Base` and use case inheriance chain loader to `Loaders::CaseBase`
|
47
|
+
- `Eco::API::Session::BatchJob` the `post_launch`:
|
48
|
+
* sets the `id` to the `person` if it was **created** successfully
|
49
|
+
* when in `dry-run` it fakes the `id` with a counter
|
50
|
+
- `Eco::API::Common::People::PersonFactory` gets `subordinates` initialized to `0` (when **creating** a `new` person)
|
51
|
+
|
52
|
+
### Fixed
|
53
|
+
|
54
|
+
|
55
|
+
## [2.0.25] - 2021-06-23
|
5
56
|
|
6
57
|
### Added
|
7
58
|
- `Eco::API::UseCases::DefaultCases::HrisCase` validation error to require `-schema-id` command line when there are people in schemas other than the active one
|
8
|
-
|
59
|
+
|
9
60
|
### Changed
|
10
61
|
- `Eco::API::Session::Batch::Job`
|
11
62
|
* for backwards compatibility `-include-only-excluded` should bring an options structure compatible with `-include-excluded`
|
@@ -19,7 +70,6 @@ All notable changes to this project will be documented in this file.
|
|
19
70
|
- `Eco::API::Session::Batch::Job` made **native** `-include-excluded`
|
20
71
|
* also added new option `-include-only-excluded` to be able to only target people HRIS excluded
|
21
72
|
|
22
|
-
|
23
73
|
## [2.0.23] - 2021-06-22
|
24
74
|
|
25
75
|
### Added
|
@@ -51,8 +101,6 @@ All notable changes to this project will be documented in this file.
|
|
51
101
|
* we just kept `roo` and `roo-xls`
|
52
102
|
- custom `Error` classes now all inherit from `StandardError` (rather than `Exception`)
|
53
103
|
|
54
|
-
|
55
|
-
|
56
104
|
## [2.0.21] - 2021-06-04
|
57
105
|
|
58
106
|
### Added
|
@@ -63,7 +111,6 @@ All notable changes to this project will be documented in this file.
|
|
63
111
|
### Changed
|
64
112
|
- `Eco::API::Common::People::EntryFactory` slight **refactor** to boost better support for multiple input formats
|
65
113
|
|
66
|
-
|
67
114
|
## [2.0.20] - 2021-05-31
|
68
115
|
|
69
116
|
### Added
|
data/lib/eco/api/common.rb
CHANGED
@@ -10,7 +10,6 @@ require_relative 'common/class_helpers'
|
|
10
10
|
require_relative 'common/class_auto_loader'
|
11
11
|
require_relative 'common/class_hierarchy'
|
12
12
|
require_relative 'common/class_meta_basics'
|
13
|
-
require_relative 'common/base_loader'
|
14
13
|
require_relative 'common/loaders'
|
15
14
|
require_relative 'common/session'
|
16
15
|
require_relative 'common/people'
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Eco
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module Loaders
|
5
|
+
class Base
|
6
|
+
extend Eco::API::Common::ClassHelpers
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Sort order
|
10
|
+
def <=>(other)
|
11
|
+
created_at <=> other.created_at
|
12
|
+
end
|
13
|
+
|
14
|
+
# If still not set, it sets the `created_at` class timestamp.
|
15
|
+
def set_created_at!
|
16
|
+
@created_at = Time.now unless @created_at
|
17
|
+
end
|
18
|
+
|
19
|
+
# Class creation timestamp, to be able to load them in the order they were declared.
|
20
|
+
def created_at
|
21
|
+
@created_at ||= Time.now
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# This method will be called when the BaseLoader is created
|
26
|
+
# @note
|
27
|
+
# - this method should implement the loading logics for the given `Children` class.
|
28
|
+
def initialize
|
29
|
+
raise "You should implement this method"
|
30
|
+
end
|
31
|
+
|
32
|
+
def name
|
33
|
+
self.class.name
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def session
|
39
|
+
ASSETS.session
|
40
|
+
end
|
41
|
+
|
42
|
+
def config
|
43
|
+
session.config
|
44
|
+
end
|
45
|
+
|
46
|
+
def logger
|
47
|
+
session.logger
|
48
|
+
end
|
49
|
+
|
50
|
+
def micro
|
51
|
+
session.micro
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Eco
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module Loaders
|
5
|
+
class CaseBase < Loaders::Base
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_writer :name, :type
|
9
|
+
|
10
|
+
# The name that this case, policy or error handler will have.
|
11
|
+
def name(value = nil)
|
12
|
+
name_only_once! if value
|
13
|
+
set_created_at!
|
14
|
+
return @name ||= self.to_s unless value
|
15
|
+
@name = value
|
16
|
+
end
|
17
|
+
|
18
|
+
# Prevent the same class to be re-opened/re-named
|
19
|
+
def name_only_once!
|
20
|
+
raise "You have already declared #{self} or you are trying to give it a name twice" if @name
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def name
|
26
|
+
self.class.name
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -2,7 +2,7 @@ module Eco
|
|
2
2
|
module API
|
3
3
|
module Common
|
4
4
|
module Loaders
|
5
|
-
class ErrorHandler < Eco::API::Common::
|
5
|
+
class ErrorHandler < Eco::API::Common::Loaders::CaseBase
|
6
6
|
|
7
7
|
class << self
|
8
8
|
attr_writer :error
|
@@ -17,7 +17,7 @@ module Eco
|
|
17
17
|
end
|
18
18
|
|
19
19
|
inheritable_class_vars :error
|
20
|
-
|
20
|
+
|
21
21
|
def initialize(handlers)
|
22
22
|
raise "Expected Eco::API::Policies. Given #{handlers.class}" unless handlers.is_a?(Eco::API::Error::Handlers)
|
23
23
|
handlers.on(self.error, &self.method(:main))
|
@@ -2,7 +2,28 @@ module Eco
|
|
2
2
|
module API
|
3
3
|
module Common
|
4
4
|
module Loaders
|
5
|
-
class Parser < Eco::API::Common::
|
5
|
+
class Parser < Eco::API::Common::Loaders::CaseBase
|
6
|
+
|
7
|
+
# Helper class to scope what required attributes it depends on
|
8
|
+
class RequiredAttrs < Struct.new(:attr, :type, :attrs)
|
9
|
+
def active?(*input_attrs)
|
10
|
+
missing(*input_attrs).empty?
|
11
|
+
end
|
12
|
+
|
13
|
+
def dependant?(attr)
|
14
|
+
attrs.include?(attr)
|
15
|
+
end
|
16
|
+
|
17
|
+
def missing(*input_attrs)
|
18
|
+
return [] if input_attrs.include?(attr)
|
19
|
+
match = input_attrs & attrs
|
20
|
+
miss = attrs - match
|
21
|
+
return [] if miss.empty?
|
22
|
+
return attrs if match.empty?
|
23
|
+
return miss if type == :all
|
24
|
+
[]
|
25
|
+
end
|
26
|
+
end
|
6
27
|
|
7
28
|
class << self
|
8
29
|
attr_reader :active_when
|
@@ -19,12 +40,14 @@ module Eco
|
|
19
40
|
@attribute = value
|
20
41
|
end
|
21
42
|
|
22
|
-
# TODO: it migh rather merge?
|
23
43
|
# Some parsers require dependencies to do their job.
|
24
|
-
def dependencies(value
|
44
|
+
def dependencies(**value)
|
25
45
|
@dependencies ||= {}
|
26
|
-
return @dependencies
|
27
|
-
|
46
|
+
return @dependencies.merge({
|
47
|
+
required_attrs: @active_when_attrs
|
48
|
+
}) unless !value.empty?
|
49
|
+
raise "Expected Hash. Given: '#{value.class}'" unless value.is_a?(Hash)
|
50
|
+
@dependencies.merge!(value)
|
28
51
|
end
|
29
52
|
|
30
53
|
# Define or get the `phase` that the `parser` kicks in.
|
@@ -47,6 +70,7 @@ module Eco
|
|
47
70
|
|
48
71
|
# Helper to build the `active_when` condition.
|
49
72
|
def active_when_any(*attrs)
|
73
|
+
@active_when_attrs = RequiredAttrs.new(attribute, :any, attrs)
|
50
74
|
@active_when = Proc.new do |source_data|
|
51
75
|
keys = data_keys(source_data)
|
52
76
|
attrs.any? {|key| keys.include?(key)}
|
@@ -55,6 +79,7 @@ module Eco
|
|
55
79
|
|
56
80
|
# Helper to build the `active_when` condition.
|
57
81
|
def active_when_all(*attrs)
|
82
|
+
@active_when_attrs = RequiredAttrs.new(attribute, :all, attrs)
|
58
83
|
@active_when = Proc.new do |source_data|
|
59
84
|
keys = data_keys(source_data)
|
60
85
|
attrs.all? {|key| keys.include?(key)}
|
@@ -2,7 +2,7 @@ module Eco
|
|
2
2
|
module API
|
3
3
|
module Common
|
4
4
|
module Loaders
|
5
|
-
class Policy < Eco::API::Common::
|
5
|
+
class Policy < Eco::API::Common::Loaders::CaseBase
|
6
6
|
|
7
7
|
def initialize(policies)
|
8
8
|
raise "Expected Eco::API::Policies. Given #{policies.class}" unless policies.is_a?(Eco::API::Policies)
|
@@ -2,7 +2,7 @@ module Eco
|
|
2
2
|
module API
|
3
3
|
module Common
|
4
4
|
module Loaders
|
5
|
-
class UseCase < Eco::API::Common::
|
5
|
+
class UseCase < Eco::API::Common::Loaders::CaseBase
|
6
6
|
|
7
7
|
class << self
|
8
8
|
# @return [Symbol] the `type` of usecase (i.e. `:sync`, `:transform`, `:import`, `:other`)
|
@@ -2,7 +2,9 @@ class Eco::API::Common::People::DefaultParsers::CSVParser < Eco::API::Common::Lo
|
|
2
2
|
attribute :csv
|
3
3
|
|
4
4
|
def parser(data, deps)
|
5
|
-
Eco::CSV.parse(data, headers: true, skip_blanks: true).
|
5
|
+
Eco::CSV.parse(data, headers: true, skip_blanks: true).tap do |table|
|
6
|
+
check_headers(table) if deps[:check_headers]
|
7
|
+
end.each_with_object([]) do |row, arr_hash|
|
6
8
|
row_hash = row.headers.uniq.each_with_object({}) do |attr, hash|
|
7
9
|
next if attr.to_s.strip.empty?
|
8
10
|
hash[attr.strip] = parse_string(row[attr])
|
@@ -36,4 +38,94 @@ class Eco::API::Common::People::DefaultParsers::CSVParser < Eco::API::Common::Lo
|
|
36
38
|
["NULL"].any? {|token| str == token}
|
37
39
|
end
|
38
40
|
|
41
|
+
def check_headers(table)
|
42
|
+
headers = table.headers
|
43
|
+
missing = missing_headers(headers)
|
44
|
+
unknown = unknown_headers(headers)
|
45
|
+
unless missing.empty? && unknown.empty?
|
46
|
+
msg = "Detected possible HEADER ISSUES !!!\n"
|
47
|
+
msg << "There might be Missing or Wrong HEADER names in the CSV file:\n"
|
48
|
+
msg << " * UNKNOWN (or not used?): #{unknown}\n" unless unknown.empty?
|
49
|
+
msg << " * MISSING DIRECT: #{missing[:direct]}\n" unless (missing[:direct] || []).empty?
|
50
|
+
unless (data = missing[:indirect] || []).empty?
|
51
|
+
msg << " * MISSING INDIRECT:\n"
|
52
|
+
data.each do |ext, info|
|
53
|
+
msg << " - '#{ext}' => "
|
54
|
+
msg << (info[:attrs] || {}).map do |status, attrs|
|
55
|
+
if status == :inactive
|
56
|
+
"makes inactive: #{attrs}"
|
57
|
+
elsif status == :active
|
58
|
+
"there could be missing info in: #{attrs}"
|
59
|
+
end
|
60
|
+
end.compact.join("; ") + "\n"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
logger.warn(msg)
|
64
|
+
sleep(2)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def unknown_headers(headers)
|
69
|
+
(headers - known_headers) - all_internal_attrs
|
70
|
+
end
|
71
|
+
|
72
|
+
def missing_headers(headers)
|
73
|
+
hint = headers & all_internal_attrs
|
74
|
+
hext = headers - hint
|
75
|
+
int_head = hint + hext.map {|e| fields_mapper.to_internal(e)}.compact
|
76
|
+
known_as_int = known_headers.select do |e|
|
77
|
+
i = fields_mapper.to_internal(e)
|
78
|
+
int_head.include?(i)
|
79
|
+
end
|
80
|
+
ext = headers.select do |e|
|
81
|
+
i = fields_mapper.to_internal(e)
|
82
|
+
int_head.include?(i)
|
83
|
+
end
|
84
|
+
ext_present = known_as_int | ext
|
85
|
+
ext_miss = known_headers - ext_present
|
86
|
+
#int_miss = ext_miss.map {|ext| fields_mapper.to_internal(ext)}
|
87
|
+
ext_miss.each_with_object({}) do |ext, missing|
|
88
|
+
next unless int = fields_mapper.to_internal(ext)
|
89
|
+
if all_internal_attrs.include?(int)
|
90
|
+
missing[:direct] ||= []
|
91
|
+
missing[:direct] << ext
|
92
|
+
end
|
93
|
+
related_attrs_requirements = required_attrs.values.select do |req|
|
94
|
+
req.dependant?(int) && !int_head.include?(req.attr)
|
95
|
+
end
|
96
|
+
next if related_attrs_requirements.empty?
|
97
|
+
missing[:indirect] ||= {}
|
98
|
+
data = missing[:indirect][ext] = {}
|
99
|
+
data[:int] = int
|
100
|
+
data[:attrs] = {}
|
101
|
+
related_attrs_requirements.each_with_object(data[:attrs]) do |req, attrs|
|
102
|
+
status = req.active?(*int_head) ? :active : :inactive
|
103
|
+
attrs[status] ||= []
|
104
|
+
attrs[status] << req.attr
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def known_headers
|
110
|
+
@known_headers ||= fields_mapper.list(:external).compact
|
111
|
+
end
|
112
|
+
|
113
|
+
def fields_mapper
|
114
|
+
session.fields_mapper
|
115
|
+
end
|
116
|
+
|
117
|
+
def required_attrs
|
118
|
+
@required_attrs ||= person_parser.required_attrs.each_with_object({}) do |ra, out|
|
119
|
+
out[ra.attr] = ra
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def all_internal_attrs
|
124
|
+
person_parser.all_attrs(include_defined_parsers: true)
|
125
|
+
end
|
126
|
+
|
127
|
+
def person_parser
|
128
|
+
session.entry_factory.person_parser
|
129
|
+
end
|
130
|
+
|
39
131
|
end
|
@@ -5,6 +5,42 @@ module Eco
|
|
5
5
|
# Class meant to offer a _collection_ of entries, normally used to get parsed input data.
|
6
6
|
# @attr_reader entries [Array<Eco::API::Common::PeopleEntry] a pure `Array` object.
|
7
7
|
class Entries < Eco::Language::Models::Collection
|
8
|
+
# Error class that allows to handle cases where multiple entries were found for the same criterion.
|
9
|
+
# @note its main purpose to prevent the false pairing of duplicates or override information between different people.
|
10
|
+
class MultipleSearchResults < StandardError
|
11
|
+
attr_reader :candidates, :property
|
12
|
+
# @param msg [String] the basic message error.
|
13
|
+
# @param candiates [Array<PersonEntry>] the entries that match the same search criterion.
|
14
|
+
# @param property [String] the property of the entry model that triggered the error (base of the search criterion).
|
15
|
+
def initialize(msg, candidates: [], property: "email")
|
16
|
+
@candidates = candidates
|
17
|
+
@property = property
|
18
|
+
super(msg + " " + candidates_summary)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param with_index [Boolean] to add an index to each candidate description.
|
22
|
+
# @return [Array<String>] the `candidates` identified
|
23
|
+
def identify_candidates(with_index: false)
|
24
|
+
candidates.map.each_with_index do |entry, i|
|
25
|
+
index = with_index ? "#{i}. " : ""
|
26
|
+
"#{index} #{entry.identify}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Person] the `candidate` in the `index` position
|
31
|
+
def candidate(index)
|
32
|
+
candidates[index]
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def candidates_summary
|
38
|
+
lines = ["The following entries have the same '#{property}':"]
|
39
|
+
lines.concat(identify_candidates(with_index: true)).join("\n ")
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
8
44
|
# build the shortcuts of Collection
|
9
45
|
attr_collection :id, :external_id, :email, :name, :supervisor_id
|
10
46
|
|
@@ -54,19 +90,34 @@ module Eco
|
|
54
90
|
# @!group Searchers
|
55
91
|
|
56
92
|
# Search function to find an `entry` based on one of different options
|
93
|
+
# It searches an entry using the parameters given.
|
94
|
+
# @note This is how the search function actually works:
|
95
|
+
# 1. if eP `id` is given, returns the entry (if found), otherwise...
|
96
|
+
# 2. if `external_id` is given, returns the entry (if found), otherwise...
|
97
|
+
# 3. if `strict` is `false` and `email` is given:
|
98
|
+
# - if there is only 1 entry with that email, returns that entry, otherwise...
|
99
|
+
# - if found but, there are many candidate entries, it raises MultipleSearchResults error
|
100
|
+
# - if entry `external_id` matches `email`, returns that entry
|
101
|
+
# @raise MultipleSearchResults if there are multiple entries with the same `email`
|
102
|
+
# and there's no other criteria to find the entry. It only gets to this point if
|
103
|
+
# `external_id` was **not** provided and we are **not** in 'strict' search mode.
|
104
|
+
# However, it could be we were in `strict` mode and `external_id` was not provided.
|
105
|
+
# @param id [String] the `internal id` of the person
|
106
|
+
# @param external_id [String] the `exernal_id` of the person
|
107
|
+
# @param email [String] the `email` of the person
|
108
|
+
# @param strict [Boolean] if should perform a `:soft` or a `:strict` search. `strict` will avoid repeated email addresses.
|
109
|
+
# @return [Entry, nil] the entry we were searching, or `nil` if not found.
|
57
110
|
def entry(id: nil, external_id: nil, email: nil, strict: false)
|
58
111
|
init_caches
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
pers
|
112
|
+
# normalize values
|
113
|
+
ext_id = !external_id.to_s.strip.empty? && external_id.strip
|
114
|
+
email = !email.to_s.strip.empty? && email.downcase.strip
|
115
|
+
|
116
|
+
e = nil
|
117
|
+
e ||= @by_id[id]&.first
|
118
|
+
e ||= @by_external_id[ext_id]&.first
|
119
|
+
e ||= entry_by_email(email) unless strict && ext_id
|
120
|
+
e
|
70
121
|
end
|
71
122
|
|
72
123
|
# Search function to find an `entry` based on one of different options
|
@@ -136,15 +187,33 @@ module Eco
|
|
136
187
|
|
137
188
|
private
|
138
189
|
|
190
|
+
def entry_by_email(email, prevent_multiple_match: false)
|
191
|
+
return nil unless email
|
192
|
+
|
193
|
+
candidates = @by_email[email] || []
|
194
|
+
return candidates.first if candidates.length == 1
|
195
|
+
|
196
|
+
if prevent_multiple_match && !candidates.empty?
|
197
|
+
msg = "Multiple search results match the criteria."
|
198
|
+
raise MultipleSearchResults.new(msg, candidates: candidates, property: "email")
|
199
|
+
end
|
200
|
+
|
201
|
+
@by_external_id[email]&.first
|
202
|
+
end
|
203
|
+
|
139
204
|
def init_caches
|
140
205
|
return if @caches_init
|
141
|
-
@by_id = to_h
|
142
|
-
@by_external_id = to_h('external_id')
|
143
|
-
@by_email = to_h('email')
|
206
|
+
@by_id = no_nil_key(to_h)
|
207
|
+
@by_external_id = no_nil_key(to_h('external_id'))
|
208
|
+
@by_email = no_nil_key(to_h('email'))
|
144
209
|
@array_supers = sort_by_supervisors(@items)
|
145
210
|
@caches_init = true
|
146
211
|
end
|
147
212
|
|
213
|
+
def no_nil_key(hash)
|
214
|
+
hash.tap {|h| h.delete(nil)}
|
215
|
+
end
|
216
|
+
|
148
217
|
end
|
149
218
|
end
|
150
219
|
end
|