import_job.rb 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
  2. class ImportJob < ApplicationModel
  3. store :payload
  4. store :result
  5. scope :running, -> { where(finished_at: nil, dry_run: false).where.not(started_at: nil) }
  6. # Starts the import backend class based on the name attribute.
  7. # Import backend class is initialized with the current instance.
  8. # Logs the start and end time (if ended successfully) and logs
  9. # exceptions into result if they happen.
  10. #
  11. # @example
  12. # import = ImportJob.new(name: 'Import::Ldap', payload: Setting.get('ldap_config'))
  13. # import.start
  14. #
  15. # return [nil]
  16. def start
  17. self.started_at = Time.zone.now
  18. save
  19. instance = name.constantize.new(self)
  20. instance.start
  21. rescue => e
  22. Rails.logger.error "ImportJob '#{name}' failed: #{e.message}"
  23. Rails.logger.error e
  24. # rubocop:disable Style/RedundantSelf
  25. if !self.result.is_a?(Hash)
  26. self.result = {}
  27. end
  28. self.result[:error] = e.message
  29. # rubocop:enable Style/RedundantSelf
  30. ensure
  31. self.finished_at = Time.zone.now
  32. save
  33. end
  34. # Gets called when the Scheduler gets (re-)started and this job was still
  35. # in the queue. If `finished_at` is blank the call is piped through to
  36. # the ImportJob backend which has to decide how to go from here. The delayed
  37. # job will get destroyed if rescheduled? is not implemented
  38. # as an ImportJob backend class method.
  39. #
  40. # @see Scheduler#cleanup_delayed
  41. #
  42. # @example
  43. # import.reschedule?(delayed_job)
  44. #
  45. # return [Boolean] whether the ImportJob should get rescheduled (true) or destroyed (false)
  46. def reschedule?(delayed_job)
  47. return false if finished_at.present?
  48. instance = name.constantize.new(self)
  49. return false if !instance.respond_to?(:reschedule?)
  50. instance.reschedule?(delayed_job)
  51. end
  52. # Convenience wrapper around the start method for starting (delayed) dry runs.
  53. # Logs the start and end time (if ended successfully) and logs
  54. # exceptions into result if they happen.
  55. # Only one running or pending dry run per backend is possible at the same time.
  56. #
  57. # @param [Hash] params the params used to initialize the ImportJob instance.
  58. # @option params [Boolean] :delay Defines if job should get executed delayed. Default is true.
  59. # @example
  60. # import = ImportJob.dry_run(name: 'Import::Ldap', payload: Setting.get('ldap_config'), delay: false)
  61. #
  62. # return [nil]
  63. def self.dry_run(params)
  64. return if exists?(name: params[:name], dry_run: true, finished_at: nil)
  65. params[:dry_run] = true
  66. instance = create(params.except(:delay))
  67. if params.fetch(:delay, true)
  68. instance.delay.start
  69. else
  70. instance.start
  71. end
  72. end
  73. # Queues and starts all import backends as import jobs.
  74. #
  75. # @example
  76. # ImportJob.start_registered
  77. #
  78. # return [nil]
  79. def self.start_registered
  80. queue_registered
  81. start
  82. end
  83. # Starts all import jobs that have not started yet and are no dry runs.
  84. #
  85. # @example
  86. # ImportJob.start
  87. #
  88. # return [nil]
  89. def self.start
  90. where(started_at: nil, dry_run: false).each(&:start)
  91. end
  92. # Queues all configured import backends from Setting 'import_backends' as import jobs
  93. # that are not yet queued. Backends which are not #queueable? are skipped.
  94. #
  95. # @example
  96. # ImportJob.queue_registered
  97. #
  98. # return [nil]
  99. def self.queue_registered
  100. backends.each do |backend|
  101. # skip backends that are not "ready" yet
  102. next if !backend.constantize.queueable?
  103. # skip if no entry exists
  104. # skip if a not finished entry exists
  105. next if ImportJob.exists?(name: backend, finished_at: nil)
  106. ImportJob.create(name: backend)
  107. end
  108. end
  109. # Checks if the given import backend is valid.
  110. #
  111. # @example
  112. # ImportJob.backend_valid?('Import::Ldap')
  113. # # => true
  114. #
  115. # return [Boolean]
  116. def self.backend_valid?(backend)
  117. backend.constantize
  118. true
  119. rescue NameError
  120. false
  121. end
  122. # Returns a list of valid import backends.
  123. #
  124. # @example
  125. # ImportJob.backends
  126. # # => ['Import::Ldap', 'Import::Exchange', ...]
  127. #
  128. # return [Boolean]
  129. def self.backends
  130. Setting.get('import_backends')&.select do |backend|
  131. if !backend_valid?(backend)
  132. logger.error "Invalid import backend '#{backend}'"
  133. next
  134. end
  135. # skip deactivated backends
  136. next if !backend.constantize.active?
  137. true
  138. end || []
  139. end
  140. end