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 # @retrun [String] the name of the current environment def enviro_name config.active_enviro end # @return [Eco::API::Session::Batch] provides helper to launch batch operations. def batch @batch ||= Batch.new(enviro) end # @!group Pure organization helpers # @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#live_tree def live_tree config.live_tree end # @see Eco::API::Session::Config#schemas def schemas config.schemas end # @!endgroup # @!group People and Input entries helpers # @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 value [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 # Helper to state the abilities that a person should have with given their usergroups def presets_factory @presets_factory ||= Eco::API::Organization::PresetsFactory.new(enviro: enviro) end # @return [Eco::Data::Mapper] the mappings between the internal and external attribute/property names. def fields_mapper return @fields_mapper if instance_variable_defined?(:@fields_mapper) mappings = [] if map_file = config.people.fields_mapper mappings = map_file ? file_manager.load_json(map_file) : [] end @fields_mapper = Eco::Data::Mapper.new(mappings) 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) unless @entry_factories.empty? @entry_factories[schema&.id] = @entry_factories.values.first.newFactory(schema: schema) return @entry_factories[schema&.id] end @entry_factories[schema&.id] = Eco::API::Common::People::EntryFactory.new( enviro, schema: schema, person_parser: config.people.parser, attr_map: fields_mapper ) 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. # @param phase [Symbol] the phase when this parser should be active. # @param phase [Symbol] the phase when this parser should be active. # @return [Object] the parsed attribute. def parse_attribute(attr, source, phase = :internal, deps: {}) unless parsers = entry_factory.person_parser raise "There are no parsers defined" end parsers.parse(attr, source, phase, deps: deps) end # @see Eco::API::Common::People::EntryFactory#export # @param (see Eco::API::Common::People::EntryFactory#export) def export(*args) entry_factory.export(*args) end # @see Eco::API::Common::People::EntryFactory#new # @param (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(schema: data&.details&.schema_id).new(data, dependencies: dependencies) end # @see Eco::API::Common::People::EntryFactory#entries # @param (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. # @param (see Eco::API::Session#entries) # @return [Eco::API::Common::People::Entries] collection of entries. def csv_entries(file, **kargs) kargs.merge!({ file: file, format: :csv }) return entries(**kargs) 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 fatal("You have not specified the 'discarded_people_file'") unless file = file_manager.dir.file(file) @discarded_entries = csv_entries(file) end # @!endgroup # @!group Session workflow and batch job launces # Opens up the `workflow` configuration def workflow(io:) config.workflow.tap do |wf| yield(wf, io) if block_given? end end # Does merge `Eco::API::UseCases::DefaultCases` with the custom cases. # @note # - the order matters, as a default usecase can be redefined by a custom one with same name # @return [Eco::API::UseCases] def usecases @usecases ||= config.usecases.dup.tap do |cases| all_cases = Eco::API::UseCases::DefaultCases.new.merge(config.usecases) cases.merge(all_cases) end end # Does merge `Eco::API::Policies::DefaultPolicies` with the custom policies. # @note # - the default policies are added at the end (meaning they will run after the custom policies) # @return [Eco::API::Policies] def policies @policies ||= config.policies.dup.tap do |policies| default_policies = Eco::API::Policies::DefaultPolicies.new policies.merge(default_policies) end end # Set of helpers to simplify your code # @see Eco::API::MicroCases # @return [Eco::API::MicroCases] def micro @micro ||= Eco::API::MicroCases.new(enviro) end def post_launch @post_launch ||= config.post_launch.select(usecases) end # @see Eco::API::UseCases::Case#launch def process_case(name, io: nil, type: nil, **params) args = { session: self }.merge(params) usecases.case(name, type: type).launch(io: io, **args) end # @return [Eco::API::Session::Batch::JobsGroups] def job_groups @job_groups ||= Batch::JobsGroups.new(enviro) end # It retrives the group of `Batch::Jobs` named `name`. It creates it if it doesn't exist. # @return [Eco::API::Session::Batch::Jobs] 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 # @param [see @Eco::API::Session::Batch::Jobs#new] # @return [Eco::API::Session::Batch::Job] def new_job(group, name, type, usecase, sets = [:core, :details, :account], accept_update_with_no_id = false, &block) job_group(group).new(name, usecase: usecase, type: type, sets: sets, accept_update_with_no_id: accept_update_with_no_id, &block) end # @see Eco::API::Session::Batch::JobsGroups#launch def jobs_launch(simulate: false) job_groups.launch(simulate: simulate) end # @see Eco::API::Session::Batch::JobsGroups#summary def summary job_groups.summary end # @!endgroup # @!group Additional resources # Sends an email # @see Eco::API::Common::Session::Mailer#mail # @param (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 = micro.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 # @!endgroup private # 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'