eco-helpers 2.0.16 → 2.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -6
  3. data/eco-helpers.gemspec +6 -4
  4. data/lib/eco-helpers.rb +1 -0
  5. data/lib/eco/api/common/base_loader.rb +14 -0
  6. data/lib/eco/api/common/people/default_parsers/date_parser.rb +11 -1
  7. data/lib/eco/api/common/people/default_parsers/login_providers_parser.rb +1 -1
  8. data/lib/eco/api/common/people/default_parsers/policy_groups_parser.rb +11 -11
  9. data/lib/eco/api/common/people/person_entry.rb +4 -2
  10. data/lib/eco/api/common/session/mailer.rb +0 -1
  11. data/lib/eco/api/common/session/s3_uploader.rb +0 -1
  12. data/lib/eco/api/common/session/sftp.rb +0 -1
  13. data/lib/eco/api/microcases.rb +3 -1
  14. data/lib/eco/api/microcases/append_usergroups.rb +0 -1
  15. data/lib/eco/api/microcases/people_cache.rb +2 -2
  16. data/lib/eco/api/microcases/people_load.rb +2 -2
  17. data/lib/eco/api/microcases/people_refresh.rb +2 -2
  18. data/lib/eco/api/microcases/people_search.rb +6 -6
  19. data/lib/eco/api/microcases/preserve_default_tag.rb +23 -0
  20. data/lib/eco/api/microcases/preserve_filter_tags.rb +28 -0
  21. data/lib/eco/api/microcases/preserve_policy_groups.rb +30 -0
  22. data/lib/eco/api/microcases/set_account.rb +0 -1
  23. data/lib/eco/api/organization.rb +1 -0
  24. data/lib/eco/api/organization/people.rb +7 -0
  25. data/lib/eco/api/organization/people_analytics.rb +60 -0
  26. data/lib/eco/api/organization/presets_factory.rb +22 -83
  27. data/lib/eco/api/organization/presets_integrity.json +6 -0
  28. data/lib/eco/api/organization/presets_values.json +5 -4
  29. data/lib/eco/api/policies/default_policies/99_user_access_policy.rb +0 -30
  30. data/lib/eco/api/session.rb +1 -20
  31. data/lib/eco/api/session/batch.rb +23 -7
  32. data/lib/eco/api/session/config.rb +0 -10
  33. data/lib/eco/api/session/config/people.rb +1 -17
  34. data/lib/eco/api/usecases/default_cases.rb +1 -1
  35. data/lib/eco/api/usecases/default_cases/abstract_policygroup_abilities_case.rb +1 -1
  36. data/lib/eco/api/usecases/default_cases/analyse_people_case.rb +76 -0
  37. data/lib/eco/api/usecases/default_cases/codes_to_tags_case.rb +2 -3
  38. data/lib/eco/api/usecases/default_cases/reset_landing_page_case.rb +11 -1
  39. data/lib/eco/api/usecases/default_cases/restore_db_case.rb +1 -2
  40. data/lib/eco/api/usecases/default_cases/supers_cyclic_identify_case.rb +1 -1
  41. data/lib/eco/api/usecases/default_cases/supers_hierarchy_case.rb +1 -1
  42. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +85 -27
  43. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +62 -36
  44. data/lib/eco/cli/config/default/options.rb +19 -17
  45. data/lib/eco/cli/config/default/people_filters.rb +3 -3
  46. data/lib/eco/cli/config/default/usecases.rb +66 -32
  47. data/lib/eco/cli/config/default/workflow.rb +1 -1
  48. data/lib/eco/cli/config/help.rb +1 -0
  49. data/lib/eco/cli/config/options_set.rb +106 -13
  50. data/lib/eco/cli/config/use_cases.rb +33 -33
  51. data/lib/eco/cli/scripting/args_helpers.rb +30 -3
  52. data/lib/eco/data.rb +1 -0
  53. data/lib/eco/data/crypto/encryption.rb +3 -3
  54. data/lib/eco/data/files/helpers.rb +6 -4
  55. data/lib/eco/data/fuzzy_match.rb +119 -0
  56. data/lib/eco/data/fuzzy_match/array_helpers.rb +75 -0
  57. data/lib/eco/data/fuzzy_match/chars_position_score.rb +37 -0
  58. data/lib/eco/data/fuzzy_match/ngrams_score.rb +73 -0
  59. data/lib/eco/data/fuzzy_match/pairing.rb +102 -0
  60. data/lib/eco/data/fuzzy_match/result.rb +67 -0
  61. data/lib/eco/data/fuzzy_match/results.rb +53 -0
  62. data/lib/eco/data/fuzzy_match/score.rb +44 -0
  63. data/lib/eco/data/fuzzy_match/stop_words.rb +35 -0
  64. data/lib/eco/data/fuzzy_match/string_helpers.rb +69 -0
  65. data/lib/eco/version.rb +1 -1
  66. metadata +82 -10
  67. data/lib/eco/api/microcases/refresh_abilities.rb +0 -19
  68. data/lib/eco/api/organization/presets_reference.json +0 -59
  69. data/lib/eco/api/usecases/default_cases/refresh_abilities_case.rb +0 -30
@@ -1,6 +1,6 @@
1
1
  ASSETS.cli.config do |cnf|
2
2
  cnf.options_set do |options_set, options|
3
- options_set.add("--help", "offers a help") do |options, sesssion|
3
+ options_set.add("--help", "Offers a HELP") do |options, sesssion|
4
4
  conf = ASSETS.cli.config
5
5
  puts conf.people_filters.help if hpf = SCR.get_arg("-filters")
6
6
  puts conf.input_filters.help if hif = SCR.get_arg("-input-filters")
@@ -11,12 +11,14 @@ ASSETS.cli.config do |cnf|
11
11
  " -filters to display available filters on people",
12
12
  " -input-filters to display available filters on input data",
13
13
  " -options to dislpay available options",
14
- " -usecases to display available usecases"
14
+ " -usecases to display available usecases",
15
+ "",
16
+ "You may specify the usecase to know its specific options by: -usecase_name --help -options"
15
17
  ].join("\n") unless hpf || hif || ho || huc
16
18
  exit
17
19
  end
18
20
 
19
- desc = "fix the current session to work with this schema"
21
+ desc = "Fix the current session to work with this schema"
20
22
  options_set.add("-schema-id", desc) do |options, session|
21
23
  sch_name = SCR.get_arg("-schema-id", with_param: true)
22
24
  sch_id = session.schemas.to_id(sch_name)
@@ -33,67 +35,67 @@ ASSETS.cli.config do |cnf|
33
35
  session.schema = sch_id
34
36
  end
35
37
 
36
- desc = "deprecated: used to be used to specify the input file when using -get-partial"
38
+ desc = "Deprecated: used to be used to specify the input file when using -get-partial"
37
39
  options_set.add("-entries-from", desc) do |options, session|
38
40
  options.deep_merge!(input: {entries_from: true})
39
41
  end
40
42
 
41
- desc = "locally cache all the people manager by retrieving from the server"
43
+ desc = "Locally cache all the people manager by retrieving from the server"
42
44
  options_set.add("-get-people", desc) do |options, session|
43
45
  options.deep_merge!(people: {
44
46
  get: {from: :remote, type: :full}
45
47
  })
46
48
  end
47
49
 
48
- options_set.add(["-dry-run", "-simulate"], "runs in dry-run (no requests sent to server)") do |options, session|
50
+ options_set.add(["-dry-run", "-simulate"], "Runs in dry-run (no requests sent to server)") do |options, session|
49
51
  options[:dry_run] = true
50
52
  options[:simulate] = true
51
53
  session.config.dry_run!
52
54
  end
53
55
 
54
- desc = "ignores threshold limitations on requests for this session (skip batch belt)"
56
+ desc = "Ignores threshold limitations on requests for this session (skip batch belt)"
55
57
  options_set.add("-skip-batch-policy", desc) do |options|
56
58
  options.deep_merge!(skip: {batch_policy: true})
57
59
  end
58
60
 
59
- desc = "will not run the api policies defined for the enviro"
61
+ desc = "Will not run the api policies defined for the enviro"
60
62
  options_set.add("-skip-api-policies", desc) do |options|
61
63
  options.deep_merge!(skip: {api_policies: true})
62
64
  end
63
65
 
64
- options_set.add("-feed-only-stats", "shows only stats when giving feedback") do |options|
66
+ options_set.add("-feed-only-stats", "Shows only stats when giving feedback") do |options|
65
67
  options.deep_merge!(feedback: {only_stats: true})
66
68
  end
67
69
 
68
- desc = "deprecated: used to be used to avoid reloading people after launch"
70
+ desc = "Deprecated: used to be used to avoid reloading people after launch"
69
71
  options[:end_get] = true
70
72
  options_set.add("-no-get", desc) do |options|
71
73
  options[:end_get] = false
72
74
  end
73
75
 
74
- desc = "force search mode to 'strict' when pairing input entries with existing people."
76
+ desc = "Force search mode to 'strict' when pairing input entries with existing people."
75
77
  desc += " Besides id and external_id it will not try to find by email unless external_id is not specified"
76
78
  options_set.add("-search-strict", desc) do |options|
77
79
  options.deep_merge!(search: {strict: true})
78
80
  end
79
81
 
80
- desc = "search mode that will try to find people using email when id and external_id have failed"
82
+ desc = "Search mode that will try to find people using email when id and external_id have failed"
81
83
  desc += " This option could identify existing people by their email addresses"
82
84
  desc += " (it should not be used in orgs where multiple people usually have the same email address)"
83
85
  options_set.add("-search-soft", desc) do |options|
84
86
  options.deep_merge!(search: {soft: true, strict: false})
85
87
  end
86
88
 
87
- desc = "silence notifications on account creation or invites"
89
+ desc = "Silence notifications on account creation or invites"
88
90
  options_set.add(["-no-invites", "-exclude-invites"], desc) do |options|
89
91
  options.merge!(send_invites: false)
90
92
  end
91
- desc = "people with account will be reinvited if they haven't accepted the invitation"
93
+ desc = "People with account will be reinvited if they haven't accepted the invitation"
92
94
  options_set.add("-send-invites", desc) do |options|
93
95
  options.merge!(send_invites: true)
94
96
  end
95
97
 
96
- options_set.add("-exclude-core", "core input data is not used on the update") do |options|
98
+ options_set.add("-exclude-core", "Core input data is not used on the update") do |options|
97
99
  options.deep_merge!(exclude: {core: true})
98
100
  end
99
101
  options_set.add("-exclude-filter-tags", "filter_tags is not set with the input data") do |options|
@@ -112,11 +114,11 @@ ASSETS.cli.config do |cnf|
112
114
  options.deep_merge!(exclude: {name: true})
113
115
  end
114
116
 
115
- options_set.add("-exclude-details", "details are not set with the input data") do |options|
117
+ options_set.add("-exclude-details", "Details are not set with the input data") do |options|
116
118
  options.deep_merge!(exclude: {details: true})
117
119
  end
118
120
 
119
- options_set.add("-exclude-account", "account is not set with the input data") do |options|
121
+ options_set.add("-exclude-account", "Account is not set with the input data") do |options|
120
122
  options.deep_merge!(exclude: {account: true})
121
123
  end
122
124
  options_set.add("-exclude-policy-groups", "policy_group_ids is not set with the input data") do |options|
@@ -52,7 +52,7 @@ ASSETS.cli.config do |cnf|
52
52
 
53
53
  desc = "only those that have ALL the specified tags separated by '|'"
54
54
  filters.add("-filter-tags-all", desc) do |people, session, options|
55
- tags = SCR.get_arg("-filter-tags-all", with_param: true).upcase.split("|")
55
+ tags = SCR.get_arg("-filter-tags-all", with_param: true).upcase.split("|").compact
56
56
  options.deep_merge!(input: {filter: {filter_tags: {any: tags}}})
57
57
  people.filter_tags_all(tags).tap do |filtered|
58
58
  msg = "Filtered #{filtered.count} people (out of #{people.count}) with 'all' filter_tags #{tags}"
@@ -62,7 +62,7 @@ ASSETS.cli.config do |cnf|
62
62
 
63
63
  desc = "only those that have ANY the specified tags separated by '|'"
64
64
  filters.add("-filter-tags-any", desc) do |people, session, options|
65
- tags = SCR.get_arg("-filter-tags-any", with_param: true).upcase.split("|")
65
+ tags = SCR.get_arg("-filter-tags-any", with_param: true).upcase.split("|").compact
66
66
  options.deep_merge!(input: {filter: {filter_tags_any: tags}})
67
67
  people.filter_tags_any(tags).tap do |filtered|
68
68
  msg = "Filtered #{filtered.count} people (out of #{people.count}) with 'any' filter_tags #{tags}"
@@ -72,7 +72,7 @@ ASSETS.cli.config do |cnf|
72
72
 
73
73
  desc = "only those that have ANY tag in the specified subtrees separated by '|'"
74
74
  filters.add("-filter-tags-tree", desc) do |people, session, options|
75
- top_tags = SCR.get_arg("-filter-tags-tree", with_param: true).upcase.split("|")
75
+ top_tags = SCR.get_arg("-filter-tags-tree", with_param: true).upcase.split("|").compact
76
76
  tags = top_tags.each_with_object([]) do |top, tags|
77
77
  tags.concat(session.tagtree.node(top).tags)
78
78
  end.uniq
@@ -1,21 +1,35 @@
1
1
  ASSETS.cli.config do |cnf|
2
2
  cnf.usecases do |cases|
3
3
 
4
- desc = "Draws the Supervisors hiearchy in a file (use option -to file.ext)"
4
+ desc = "Draws the Supervisors hiearchy in a file"
5
5
  cases.add("-supers-hierarchy", :export, desc, case_name: "supers-hierarchy") do |people, session, options|
6
- file = (SCR.get_arg("-to") && SCR.get_file("-to", required: true, should_exist: false)) || "supers_hierarchy.txt"
6
+ options.deep_merge!(output: {file: "supers_hierarchy.txt"}) unless options.dig(:output, :file)
7
+ end.add_option("-to", "Specify the output file") do |options|
8
+ file = SCR.get_file("-to", required: true, should_exist: false)
7
9
  options.deep_merge!(output: {file: file})
8
10
  end
9
11
 
10
- desc = "Draws the Cyclic Supervisors when identified (use option -to file.ext)"
12
+ desc = "Draws the Cyclic Supervisors when identified"
11
13
  cases.add("-identify-cyclic-supers", :export, desc, case_name: "identify-cyclic-supers") do |people, session, options|
12
- file = (SCR.get_arg("-to") && SCR.get_file("-to", required: true, should_exist: false)) || "supers_hierarchy.txt"
14
+ options.deep_merge!(output: {file: "cyclic_supers.txt"}) unless options.dig(:output, :file)
15
+ end.add_option("-to", "Specify the output file") do |options|
16
+ file = SCR.get_file("-to", required: true, should_exist: false)
13
17
  options.deep_merge!(output: {file: file})
14
18
  end
15
19
 
16
- desc = "Abstracts the Abilities that each Usergroup should probably have (use option -to file.ext)"
20
+ desc = "Abstracts the Abilities that each Usergroup should probably have"
17
21
  cases.add("-abstract-policygroup-abilities", :export, desc, case_name: "abstract-policygroup-abilities") do |people, session, options|
18
- file = (SCR.get_arg("-to") && SCR.get_file("-to", required: true, should_exist: false)) || "suggested_abilities.txt"
22
+ options.deep_merge!(output: {file: "suggested_usergroup_abilities.txt"}) unless options.dig(:output, :file)
23
+ end.add_option("-to", "Specify the output file") do |options|
24
+ file = SCR.get_file("-to", required: true, should_exist: false)
25
+ options.deep_merge!(output: {file: file})
26
+ end
27
+
28
+ desc = "Provides a set of tools to analyse a set of people (i.e. detect duplicates)"
29
+ cases.add("-analyse-people", :export, desc, case_name: "-analyse-people") do |people, session, options|
30
+ options.deep_merge!(output: {file: "people_analysis.txt"}) unless options.dig(:output, :file)
31
+ end.add_option("-to", "Specify the output file") do |options|
32
+ file = SCR.get_file("-to", required: true, should_exist: false)
19
33
  options.deep_merge!(output: {file: file})
20
34
  end
21
35
 
@@ -23,23 +37,36 @@ ASSETS.cli.config do |cnf|
23
37
  cases.add("-people-to-csv", :export, desc) do |people, session, options|
24
38
  file = SCR.get_file("-people-to-csv", required: true, should_exist: false)
25
39
  options.deep_merge!(export: {file: {name: file, format: :csv}})
26
- options.deep_merge!(export: {options: {nice_header: true}}) if SCR.get_arg("-nice-header")
27
- options.deep_merge!(export: {options: {internal_names: true}}) if SCR.get_arg("-internal-names")
28
- case_name = SCR.get_arg("-detailed")? "to-csv-detailed" : "to-csv"
40
+
41
+ case_name = options.dig(:export, :options, :detailed) ? "to-csv-detailed" : "to-csv"
29
42
  session.usecases.case(case_name)
43
+ end.add_option("-nice-header", "Outputs more descriptive standard headers") do |options|
44
+ options.deep_merge!(export: {options: {nice_header: true}})
45
+ end.add_option("-internal-names", "It is the most raw export. Useful to see all the data when name mappings override/overlap") do |options|
46
+ options.deep_merge!(export: {options: {internal_names: true}})
47
+ end.add_option("-detailed", "Includes much more information to the file (i.e. permissions_merged abilities, preferences)") do |options|
48
+ options.deep_merge!(export: {options: {detailed: true}})
49
+ end.add_option("-permissions-custom", "Used with -detailed. Adds the permissions_custom abilities") do |options|
50
+ options.deep_merge!(export: {options: {permissions_custom: true}})
30
51
  end
31
52
 
32
53
  desc = "Adds a column 'ecoPortalTag' to the input CSV with the tags that the location codes map to"
33
- cases.add("-codes-to-tags-from", :other, desc, case_name: "codes-to-tags-from") do |session, options|
54
+ cases.add("-codes-to-tags-from", :other, desc, case_name: "codes-to-tags-from")
55
+ .add_option("-codes-to-tags-from", "Specify the input 'csv' file") do |options|
34
56
  file = SCR.get_file("-codes-to-tags-from", required: true, should_exist: true)
35
57
  options.deep_merge!(other: {file: {name: file, format: :csv}})
36
-
58
+ end.add_option("-column", "Specify the input column header with the codes") do |options|
37
59
  col_codes = SCR.get_arg("-column", with_param: true)
38
60
  options.deep_merge!(other: {file: {codes_column: col_codes}})
39
61
  end
40
62
 
41
- desc = "Removes the landing page"
63
+ desc = "Removes the landing page or sets it to -page-id"
42
64
  cases.add("-reset-landing-page", :transform, desc, case_name: "reset-landing-page")
65
+ .add_option("-page-id", "Target landing page to set to the users") do |options|
66
+ SCR.get_arg("-page-id", with_param: true).tap do |new_id|
67
+ options.deep_merge!({page_id: new_id})
68
+ end
69
+ end
43
70
 
44
71
  desc = "Sets as external_id the email of the person"
45
72
  cases.add("-email-as-id", :transform, desc, case_name: "email-as-id")
@@ -54,23 +81,29 @@ ASSETS.cli.config do |cnf|
54
81
 
55
82
  desc = "Sets the supervisor_id"
56
83
  cases.add("-set-supervisor-from", :sync, desc, case_name: "set-supervisor")
84
+
57
85
  desc = "Sets to -new-super the supervisor_id of the -old-super's subordinates"
58
86
  cases.add("-switch-supervisor", :transform, desc, case_name: "switch-supervisor") do |people, session, options|
59
- unless old_id = SCR.get_arg("-old-super", with_param: true)
87
+ unless options[:super]&.key?(:old)
60
88
  msg = "You must specify an -old-super to target whose supervisor is changing"
61
89
  session.logger.error(msg)
62
90
  exit(1)
63
91
  end
64
-
65
- options.deep_merge!(super: {old: old_id})
66
-
67
- unless new_id = SCR.get_arg("-new-super", with_param: true)
92
+ unless options[:super]&.key?(:new)
68
93
  msg = "You must specify the -new-super id. To reset to nil the supervisor, please, specify nil."
69
94
  session.logger.error(msg)
70
95
  exit(1)
71
96
  end
72
- new_id = new_id == "nil"? nil : new_id
73
- options.deep_merge!(super: {new: new_id})
97
+ end.add_option("-old-super", "The supervisor id to be replaced on the subordinates") do |options|
98
+ if old_id = SCR.get_arg("-old-super", with_param: true)
99
+ old_id = old_id == "nil"? nil : old_id
100
+ options.deep_merge!(super: {old: old_id})
101
+ end
102
+ end.add_option("-new-super", "The new supervisor id") do |options|
103
+ if new_id = SCR.get_arg("-new-super", with_param: true)
104
+ new_id = new_id == "nil"? nil : new_id
105
+ options.deep_merge!(super: {new: new_id})
106
+ end
74
107
  end
75
108
 
76
109
  desc = "Usage '-org-data-convert backup.json -restore-db-from'."
@@ -83,15 +116,15 @@ ASSETS.cli.config do |cnf|
83
116
  session.logger.info("Source DB: loaded #{input.length} entries.")
84
117
  end
85
118
 
86
- if source_enviro = SCR.get_arg("-source-enviro", with_param: true)
87
- options.merge!(source_enviro: source_enviro)
88
- else
119
+ unless options[:source_enviro]
89
120
  session.logger.error("You need to specify a -source-enviro for the conversion to work out")
90
121
  exit(1)
91
122
  end
92
123
 
93
- options.deep_merge!(ignore: {missing: {policy_groups: true}}) if SCR.get_arg("-ignore-missing-policy-groups")
94
-
124
+ end.add_option("-source-enviro", "The defined -source-enviro API configuration that the backup file was generated from") do |options|
125
+ options.merge!(source_enviro: SCR.get_arg("-source-enviro", with_param: true))
126
+ end.add_option("-ignore-missing-policy-groups", "Prevents the script to crash when backup file has missing usergroups in the org") do |options|
127
+ options.deep_merge!(ignore: {missing: {policy_groups: true}})
95
128
  end
96
129
 
97
130
  desc = "Restores the people manager by using a backup.json file"
@@ -101,13 +134,15 @@ ASSETS.cli.config do |cnf|
101
134
  input = Eco::API::Organization::People.new(JSON.parse(File.read(file)))
102
135
  session.logger.info("Source DB: loaded #{input.length} entries.")
103
136
  end
104
-
105
- options.deep_merge!(include: {delete: true}) if SCR.get_arg("-include-delete")
106
- options.deep_merge!(include: {create: true}) if SCR.get_arg("-include-create")
137
+ end.add_option("-include-delete", "If it should DELETE people that do not exist in the backup file") do |options|
138
+ options.deep_merge!(include: {delete: true})
139
+ end.add_option("-include-create", "If it should CREATE people that do not exist in the people manager") do |options|
140
+ options.deep_merge!(include: {create: true})
107
141
  end
108
142
 
109
143
  desc = "Re-sends invites to all filtered users that have not accepted the invite as yet"
110
144
  cases.add("-reinvite", :transform, desc, case_name: "reinvite")
145
+
111
146
  desc = "Re-sends invites to target users that have not accepted the invite as yet"
112
147
  cases.add("-reinvite-from", :sync, desc, case_name: "reinvite")
113
148
 
@@ -130,8 +165,6 @@ ASSETS.cli.config do |cnf|
130
165
 
131
166
  desc = "It just adds everybody to an update job without doing any change. If the org has policies, it will refresh"
132
167
  cases.add("-refresh", :transform, desc, case_name: "refresh")
133
- desc = "Remaps the abilities of every user based on their usergroups mappings thereof in the org api configuration"
134
- cases.add("-refresh-abilities", :transform, desc, case_name: "refresh-abilities")
135
168
 
136
169
  desc = "Updates details and core (including supervisor) to target people"
137
170
  cases.add("-update-details-from", :sync, desc, case_name: "update-details")
@@ -141,9 +174,10 @@ ASSETS.cli.config do |cnf|
141
174
  desc = "Updates the people specified in the input data"
142
175
  cases.add("-update-from", :sync, desc, case_name: "update")
143
176
 
144
- desc = "Does an actual transfer of user from id|external_id account to 'destination-id' person"
145
- cases.add("-transfer-account-from", :sync, desc, case_name: "transfer-account") do |input, people, session, options|
146
- options.deep_merge!(include: {email: SCR.get_arg("-include-email")})
177
+ desc = "Does an actual transfer of USER from id|external_id account to 'destination-id' person"
178
+ cases.add("-transfer-account-from", :sync, desc, case_name: "transfer-account")
179
+ .add_option("-include-email", "Specifies if the email should also be moved. Otherwise it only moves the account") do |options|
180
+ options.deep_merge!(include: {email: true})
147
181
  end
148
182
 
149
183
  desc = "Tries to find the input entries and update them. It creates them if not found"
@@ -29,7 +29,7 @@ ASSETS.cli.config do |config|
29
29
  if io.options.dig(:input, :entries_from)
30
30
  io = io.new(input: config.input.get(io: io))
31
31
  else
32
- opt_case = cases_with_input.values.first[:option]
32
+ opt_case = cases_with_input.values.first.option
33
33
  io = io.new(input: config.input.get(io: io, option: opt_case))
34
34
  end
35
35
  io
@@ -16,6 +16,7 @@ module Eco
16
16
  # Creatas a well aligned line
17
17
  def help_line(key, desc, keys_max_len = key.length, line_len = 100)
18
18
  blanks = keys_max_len + 3 - key.length
19
+ blanks = blanks < 0 ? 0 : blanks
19
20
  top_line = " #{key}#{" "*blanks} "
20
21
  indent = top_line.length
21
22
  first = true
@@ -5,31 +5,53 @@ module Eco
5
5
  include Eco::CLI::Config::Help
6
6
  attr_reader :core_config
7
7
 
8
+ class OptConfig < Struct.new(:name, :namespace, :description, :callback)
9
+ end
10
+
8
11
  def initialize(core_config:)
9
12
  @core_config = core_config
10
- @options_set = {}
11
- @description = {}
13
+ @sets = {}
12
14
  end
13
15
 
14
16
  # @return [String] summary of the options.
15
17
  def help
18
+ indent = 2
19
+ spaces = any_non_general_space_active? ? active_namespaces : namespaces
20
+
16
21
  ["The following are the available options:"].yield_self do |lines|
17
- max_len = keys_max_len(@options_set.keys)
18
- @options_set.keys.each do |key|
19
- lines << help_line(key, @description[key], max_len)
22
+ max_len = keys_max_len(options_args(spaces)) + indent
23
+ spaces.each do |namespace|
24
+ is_general = (namespace == :general)
25
+ str_indent = is_general ? "" : " " * indent
26
+ lines << help_line(namespace, "", max_len) unless is_general
27
+ options_set(namespace).each do |arg, option|
28
+ lines << help_line(" " * indent + "#{option.name}", option.description, max_len)
29
+ end
20
30
  end
21
31
  lines
22
32
  end.join("\n")
23
33
  end
24
34
 
25
- # @param option [String] the command line option.
35
+ # @return [Array<String>] all the argument of the options in `namespaces`
36
+ def options_args(namespaces)
37
+ namespaces.each_with_object([]) do |space, args|
38
+ args.concat(options_set(space).keys)
39
+ end.uniq
40
+ end
41
+
42
+ # @param option [String, Array<String>] the command line option(s).
43
+ # @param namespace [String] preceding command(s) argument that enables this option.
26
44
  # @param desc [String] description of the option.
27
- def add(option, desc = nil)
45
+ def add(option, desc = nil, namespace: :general)
28
46
  raise "Missing block to define the options builder" unless block_given?
29
- callback = Proc.new
30
- [option].flatten.compact.each do |opt|
31
- @options_set[opt] = callback
32
- @description[opt] = desc
47
+
48
+ opts = [option].flatten.compact
49
+ unless opts.empty?
50
+ callback = Proc.new
51
+ opts.each do |opt|
52
+ puts "Overriding option '#{option}' in '#{namespace}' namespace" if option_exists?(opt, namespace)
53
+ options_set(namespace)[opt] = OptConfig.new(opt, namespace, desc, callback)
54
+ end
33
55
  end
34
56
  self
35
57
  end
@@ -39,12 +61,83 @@ module Eco
39
61
  raise "You need to provide Eco::API::UseCases::BaseIO object. Given: #{io.class}"
40
62
  end
41
63
 
42
- @options_set.each do |arg, callback|
43
- callback.call(io.options, io.session) if SCR.get_arg(arg)
64
+ active_options.each do |option|
65
+ option.callback.call(io.options, io.session)
44
66
  end
67
+
45
68
  io.options
46
69
  end
47
70
 
71
+ def active_options
72
+ @active_options ||= sets.select do |namespace, opts_set|
73
+ active_namespace?(namespace)
74
+ end.each_with_object([]) do |(namespace, opts_set), options|
75
+ opts_set.each do |arg, option|
76
+ options << option if active_option?(arg, namespace)
77
+ end
78
+ end
79
+ end
80
+
81
+ def all_options
82
+ sets.each_with_object([]) do |(namespace, opts_set), options|
83
+ options << opts_set.values
84
+ end
85
+ end
86
+
87
+ def namespaces
88
+ sets.keys.sort_by do |key|
89
+ key == :general
90
+ end
91
+ end
92
+
93
+ def any_non_general_space_active?
94
+ (active_namespaces - [:general]).length > 0
95
+ end
96
+
97
+ def active_namespaces
98
+ @active_namespaces ||= [].tap do |active|
99
+ active << :general
100
+ other = (namespaces - [:general]).select {|nm| SCR.arg?(nm)}
101
+ active.concat(other)
102
+ end
103
+ end
104
+
105
+
106
+ private
107
+
108
+ def active_namespace?(namespace)
109
+ (namespace == :general) || SCR.get_arg(namespace)
110
+ end
111
+
112
+ # Is the option active?
113
+ # 1. If :general namespace, it does just a direct check
114
+ # 2. Otherwise, the `namespace` wording should come first in the `cli` or it is considered inactive
115
+ def active_option?(opt, namespace = :general)
116
+ if namespace == :general
117
+ SCR.get_arg(opt)
118
+ else
119
+ active_namespace?(namespace) && SCR.arg_order?(namespace, opt) && SCR.get_arg(opt)
120
+ end
121
+ end
122
+
123
+ def option_exists?(opt, namespace = :general)
124
+ options_set(namespace).key?(opt)
125
+ end
126
+
127
+ def sets
128
+ @sets ||= {
129
+ general: {}
130
+ }
131
+ end
132
+
133
+ def namespaces
134
+ @sets.keys
135
+ end
136
+
137
+ def options_set(namespace = :general)
138
+ @sets[namespace] ||= {}
139
+ end
140
+
48
141
  end
49
142
  end
50
143
  end