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 = {}) env = init msg = "Expected object Eco::API::Session::Config or Eco::API::Common::Session::Environment. " msg << " Given: #{init}" correct_env = env.is_a?(Session::Config) || env.is_a?(Eco::API::Common::Session::Environment) raise ArgumentError, msg unless correct_env unless env.is_a?(Eco::API::Common::Session::Environment) env = Eco::API::Common::Session::Environment.new(init, session: self) end super(env) @entry_factories = {} @person_factories = {} log(:debug) { "LINE COMMAND: #{$0} #{ARGV.join(" ")}" # rubocop:disable Style/SpecialGlobalVars } 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 # The location node classifications of the organization # @return [Eco::API::Organization::NodeClassifications] def node_classifications(active: true) config.node_classifications(active: active) end # @param live [Boolean] states preference of `live` tree if available over file. # @param merge [Boolean] when in `live` mode, if mulitple trees, merge into one. # @see Eco::API::Session::Config#tagtree # @see Eco::API::Session#live_trees # @return [Eco::API::Organization::TagTree] def tagtree( live: false, merge: false, include_archived: false, recache: false, **kargs, &block ) if live && api?(version: :graphql) return live_tree(include_archived: include_archived, **kargs, &block) unless merge live_trees(include_archived: include_archived, **kargs, &block).inject(&:merge) else config.tagtree(recache: recache) end end # @see Eco::API::Session::Config#live_tree # @return [Eco::API::Organization::TagTree] def live_tree(id: nil, include_archived: false, **kargs, &block) config.live_tree(id: id, include_archived: include_archived, **kargs, &block) end # @see Eco::API::Session::Config#live_tree def live_trees(include_archived: false, **kargs, &block) config.live_trees(include_archived: include_archived, **kargs, &block) 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) 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, **kargs) entry_factory.entries(*args, **kargs).tap do |collection| log(: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 }) 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 file = file_manager.dir.file(file) fatal("You have not specified the 'discarded_people_file'") unless 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) return job_groups[name] if job_groups.exists?(name) job_groups.new(name, order: order) 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( # rubocop:disable Metrics/ParameterLists group, name, type, usecase, sets = %i[core details account], accept_update_with_no_id = false, # rubocop:disable Style/OptionalBooleanParameter &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 log(: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 log(: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 log(: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?(schema_1, schema_2) eq = schema_1&.id == schema_2&.id eq ||= schema_1&.name&.downcase == schema_2&.name&.downcase schema_1 && schema_2 && 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'