module Eco module API # Class to manage the current session. # Central helper of resources. class Session < Common::Session::BaseSession #@param init [Eco::API::Session::Config, Eco::API::Common::Session::Environment] object to ini the session def initialize(init = {}) e = init msg = "Expected object Eco::API::Session::Config or Eco::API::Common::Session::Environment. Given: #{init}" raise msg unless e.is_a?(Session::Config) || e.is_a?(Eco::API::Common::Session::Environment) e = Eco::API::Common::Session::Environment.new(init, session: self) if !e.is_a?(Eco::API::Common::Session::Environment) super(e) @entry_factories = {} @person_factories = {} logger.debug("LINE COMMAND: #{$0} #{ARGV.join(" ")}") end # Helper to perform multiple operations in one go. # @return [Eco::API::Session::Task] provides shortcuts to manage certain kind of operations. def do @task ||= Task.new(enviro) end # @return [Eco::API::Session::Batch] provides helper to launch batch operations. def batch @batch ||= Batch.new(enviro) end # @see Eco::API::Session::Config#policy_groups def policy_groups config.policy_groups end # @see Eco::API::Session::Config#login_providers def login_providers config.login_providers end # @see Eco::API::Session::Config#tagtree def tagtree config.tagtree(enviro: enviro) end # @see Eco::API::Session::Config#schemas def schemas config.schemas end # @return [String, Ecoportal::API::V1::PersonSchema] current active session's schema def schema self.schema = config.people.default_schema || schemas.first unless @schema @schema end # Sets the current target `PersonSchema` of this session. # @note observe that it is essential for the parsers/serialisers to identify target/present attributes. # @param schema [String, Ecoportal::API::V1::PersonSchema] where `String` can be the _name_ or the _id_ of the schema. def schema=(value) @schema = to_schema(value) self end # Builds the presets using the usergroup ids of the input. # @note for each flag/ability it will take the highest among those mapped for the present usergroups. # @param [Ecoportal::API::Internal::Person, Array] the array should be of usegroup names or ids. # @return [Hash] with custom presets. def new_preset(input) case input when Ecoportal::API::Internal::Person presets_factory.new(*input&.account&.policy_group_ids) when Array presets_factory.new(*input) else presets_factory.new(input) end end # Helper to obtain a EntryFactory # @param schema [String, Ecoportal::API::V1::PersonSchema] `schema` to which associate the EntryFactory, # where `String` can be the _name_ or the _id_ of the schema. # @return [Eco::API::Common::People::EntryFactory] associated to `schema`. # If `schema` is `nil` or not provided it uses the currently associated to the `session` def entry_factory(schema: nil) schema = to_schema(schema) || self.schema return @entry_factories[schema&.id] if @entry_factories.key?(schema&.id) mappings = [] if map_file = config.people.fields_mapper mappings = map_file ? file_manager.load_json(map_file) : [] end @entry_factories[schema&.id] = Eco::API::Common::People::EntryFactory.new( enviro, schema: schema, person_parser: config.people.parser, attr_map: Eco::Data::Mapper.new(mappings) ) end # Helper to obtain a PersonFactory # @param schema [String, Ecoportal::API::V1::PersonSchema] `schema` to which associate the PersonFactory, # where `String` can be the _name_ or the _id_ of the schema. # @return [Eco::API::Common::People::PersonFactory] associated to `schema`. # If `schema` is `nil` or not provided it uses the currently associated to the `session` def person_factory(schema: nil) schema = to_schema(schema) || self.schema @person_factories[schema&.id] ||= Eco::API::Common::People::PersonFactory.new(schema: schema) end # Allows to use the defined parsers # @note the use of these method requires to know which is the expected format of `source` # @param attr [String] type (`Symbol`) or attribute (`String`) to target a specific parser. # @param source [Any] source value to be parsed. def parse_attribute(attr, source) unless parsers = entry_factory.person_parser raise "There are no parsers defined" end parsers.parse(attr, source) end def export(*args) entry_factory.export(*args) end # @see Eco::API::Common::People::EntryFactory#new # @return [Ecoportal::API::Internal::Person] def new_person(**keyed_args) person_factory.new(**keyed_args) end # Builds the entry for the given data. # @see Eco::API::Common::People::EntryFactory#new # @return [Eco::API::Common::People::PersonEntry] parsed entry. def new_entry(data, dependencies: {}) entry_factory.new(data, dependencies: dependencies) end # @see Eco::API::Common::People::EntryFactory#entries # @return [Eco::API::Common::People::Entries] collection of entries. def entries(*args) entry_factory.entries(*args).tap do |collection| logger.info("Loaded #{collection.length} input entries.") end end # Generates an entries collection from a csv input file. # @see Eco::API::Common::People::EntryFactory#entries # @param file [String] file to generate the entries from. # @return [Eco::API::Common::People::Entries] collection of entries. def csv_entries(file) return entries(file: file, format: :csv) end # Generates the collection of entries that should be discarded from an update. # @note requires `session.config.people.discarded_file` to be defined. # @return [Eco::API::Common::People::Entries] collection of entries. def discarded_entries return @discarded_entries if instance_variable_defined?(:@discarded_entries) file = config.people.discarded_file file = file_manager.dir.file(file) @discarded_entries = csv_entries(file) end def workflow(io:) config.workflow.tap do |wf| yield(wf, io) if block_given? end end def usecases @usecases ||= Eco::API::UseCases::DefaultCases.new.merge(config.usecases) end def post_launch @post_launch ||= config.post_launch.select(usecases) end def process_case(name, io: nil, type: nil, **params) args = { session: self }.merge(params) usecases.case(name, type: type).launch(io: io, **args) end def job_groups @job_groups ||= Batch::JobsGroups.new(enviro) end def job_group(name, order: :last) case when job_groups.exists?(name) job_groups[name] else job_groups.new(name, order: order) end end # shortcut to create a job of certain type within a group def new_job(group, name, type, usecase, sets = [:core, :details, :account]) job_group(group).new(name, usecase: usecase, type: type, sets: sets) end def jobs_launch(simulate: false) job_groups.launch(simulate: simulate) end def summary job_groups.summary end # Sends an email # @see Eco::API::Common::Session::Mailer#mail def mail(**kargs) if mailer? mailer.mail(**kargs) else logger.error("You are trying to use the mailer, but it's not configured") nil end end # Uploads content into a file, a file or a directory to S3 # @see Eco::API::Common::Session::S3Uploader#upload # @see Eco::API::Common::Session::S3Uploader#upload_file # @see Eco::API::Common::Session::S3Uploader#upload_directory # @param content [String] content to be uploaded (requires `file`) # @param file [String] name of the file to be uploaded # @param directory [String] name of source directory to be uploaded # @param recurse [Boolean] used with `directory`: deepen in the folder structure? (`false`: default) # @param link [Boolean] **return** _link(s)_ (`true`) or _path(s)_ (`false`: default) # @return [String, Array] either paths to S3 objects if `link` is `false`, or _link_ otherwise def s3upload(content: nil, file: nil, directory: nil, recurse: false, link: false) if s3uploader? if content == :target path = self.do.s3upload_targets elsif content && file path = s3uploader.upload(file, content) elsif file path = s3uploader.upload_file(file) elsif directory path = s3uploader.upload_directory(directory, recurse: recurse) else logger.error("To use Session.s3upload, you must specify either directory, file or content and file name") end return path unless link s3uploader.link(path) else logger.error("You are trying to use S3 uploader, but it's not configured") nil end end private # Helper to state the abilities that a person should have with given their usergroups def presets_factory @presets_factory ||= Eco::API::Organization::PresetsFactory.new({ presets_custom: file_manager.dir.file(config.people.presets_custom, should_exist: true), presets_map: file_manager.dir.file(config.people.presets_map, should_exist: true), enviro: enviro }) end # Comparer to state if 2 schemas are different def same_schema? (schema1, schema2) eq = schema1&.id == schema2&.id eq ||= schema1&.name&.downcase == schema2&.name&.downcase schema1 && schema2 && eq end # from schema `id` or `name` to a PersonSchema object def to_schema(value) return nil unless value sch = nil case value when String unless sch = schemas.schema(value) fatal "The schema with id or name '#{value}' does not exist." end when Ecoportal::API::V1::PersonSchema sch = value else fatal "Required String or Ecoportal::API::V1::PersonSchema. Given: #{value}" end sch end end end end require_relative 'session/config' require_relative 'session/batch' require_relative 'session/task'