lib/ree_lib/packages/ree_dao/package/ree_dao/functions/build_connection.rb



# frozen_string_literal: true

require 'time'
require 'logger'
require 'timeout'

class ReeDao::BuildConnection
  include Ree::FnDSL

  fn :build_connection do
    link :connections
    link 'ree_dao/dataset_extensions', -> { DatasetExtensions }
  end

  TIMEZONES = [:utc, :local].freeze

  DEFAULTS = {
    datetime_class: DateTime,
    database_timezone: :utc,
    application_timezone: :utc,
    typecast_timezone: :utc,
    connection_validation_timeout: 60,
    single_threaded: false,
    timeout: 90
  }.freeze

  contract(
    Hash,
    Ksplat[
      timeout?: Integer,
      convert_two_digit_years?: Bool,
      single_threaded?: Bool,
      extensions?: ArrayOf[Symbol],
      datetime_class?: Or[Time, DateTime],
      after_connect?: Proc,
      database_timezone?: Or[*TIMEZONES],
      application_timezone?: Or[*TIMEZONES],
      typecast_timezone?: Or[*TIMEZONES],
      connection_validation_timeout?: Integer,
      logger?: Logger,
      sql_log_level?: [:fatal, :error, :warn, :info, :debug],
    ] => Any
  )
  def call(conn_opts, **opts)
    opts = DEFAULTS.merge(opts.dup)

    database_timezone = opts.delete(:database_timezone)
    application_timezone = opts.delete(:application_timezone)
    typecast_timezone = opts.delete(:typecast_timezone)
    # connection_validation_timeout = opts.delete(:connection_validation_timeout)
    convert_two_digit_years = opts.delete(:convert_two_digit_years)
    single_threaded = opts.delete(:single_threaded)
    datetime_class = opts.delete(:datetime_class)
    extensions = opts.delete(:extensions) || []
    timeout = opts.delete(:timeout)

    Sequel.database_timezone = database_timezone
    Sequel.application_timezone = application_timezone
    Sequel.typecast_timezone = typecast_timezone
    Sequel.single_threaded = single_threaded
    Sequel.convert_two_digit_years = convert_two_digit_years if convert_two_digit_years
    Sequel.datetime_class = datetime_class

    connection = Sequel.connect(conn_opts)

    if opts[:logger]
      connection.logger = opts[:logger]
    end

    if opts[:sql_log_level]
      connection.sql_log_level = opts[:sql_log_level] 
    end

    Timeout::timeout(timeout) do
      loop do
        begin
          connection.test_connection
          break
        rescue => e
          puts("Unable to establish DB connection: #{conn_opts.inspect}")
          puts(e.inspect)
          sleep(1)
        end
      end
    end

    # connection.pool.connection_validation_timeout = connection_validation_timeout if connection_validation_timeout

    extensions.each { connection.extension(_1) }
    connections.add(connection)

    dataset_class = connection.dataset_class
    klass = Class.new(dataset_class)
    klass.extend(ReeDao::DatasetExtensions)

    connection.dataset_class = klass

    connection
  rescue => e
    connection&.disconnect
    raise e
  end
end