lib/canvas_sync/engine.rb



require "rails"
require 'chronic_duration'

module CanvasSync
  class Engine < ::Rails::Engine
    isolate_namespace CanvasSync

    initializer "canvas_sync.safe_yaml_classes" do |app|
      app.config.active_record.yaml_column_permitted_classes ||= []
      app.config.active_record.yaml_column_permitted_classes |= [Symbol, ActiveSupport::HashWithIndifferentAccess]
      ActiveRecord::Base.yaml_column_permitted_classes |= app.config.active_record.yaml_column_permitted_classes
    rescue
    end

    initializer :append_migrations do |app|
      config.paths["db/migrate"].expanded.each do |expanded_path|
        app.config.paths["db/migrate"] << expanded_path
      end
      # Apartment will modify this, but it doesn't fully support engine migrations, so we'll reset it here
      ActiveRecord::Migrator.migrations_paths = Rails.application.paths["db/migrate"].to_a
    end

    RETENTION_TYPE = {
      type: 'string',
      validate: ->(value, *args, errors:, **kwargs) {
        origExc = ChronicDuration.raise_exceptions
        ChronicDuration.raise_exceptions = true
        begin
          ChronicDuration.parse(value) unless value == nil
          nil
        rescue ChronicDuration.DurationParseError
          errors << "<path> must be nil or a parseable duration"
        ensure
          ChronicDuration.raise_exceptions = origExc
        end
      }
    }

    initializer 'canvas_sync.global_methods' do
      next if defined?(canvas_sync_client)

      require 'panda_pal'

      class ::Object
        def canvas_sync_client(account_id = nil)
          org = PandaPal::Organization.current
          org = org.platform_api(PandaPal::Platform::Canvas) if (PandaPal::Organization.instance_method(:platform_api) rescue nil)
          Bearcat::Client.new(
            prefix: org.canvas_url,
            token: org.canvas_api_token,
            master_rate_limit: (Rails.env.production? && !!defined?(Redis) && ENV['REDIS_URL'].present?),
          )
        end
      end
    rescue LoadError
    end

    initializer :integrate_pandapal do
      require 'panda_pal'

      Rails.application.reloader.to_prepare do
        if PandaPal::Organization.respond_to?(:scheduled_task)
          if PandaPal::Organization.respond_to?(:define_setting)
            PandaPal::Organization.define_setting(:canvas_sync, {
              type: 'Hash',
              required: false,
              properties: {
                job_log_retention: { **RETENTION_TYPE },
                sync_batch_retention: { **RETENTION_TYPE },
              }
            })
          end

          unless PandaPal::Organization.task_scheduled?(:clean_canvas_sync_logs)
            PandaPal::Organization.scheduled_task '0 0 3 * * *', :clean_canvas_sync_logs do
              job_log_retention = ChronicDuration.parse(settings.dig(:canvas_sync, :job_log_retention) || '3 months', keep_zero: true).seconds.ago
              JobLog.where('updated_at < ?', job_log_retention).delete_all

              sync_batch_retention = ChronicDuration.parse(settings.dig(:canvas_sync, :sync_batch_retention) || '6 months', keep_zero: true).seconds.ago
              SyncBatch.where('updated_at < ?', sync_batch_retention).delete_all
            end
          end
        end
      end
    rescue LoadError
    end
  end
end