opf/openproject

View on GitHub
modules/storages/app/workers/storages/copy_project_folders_job.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module Storages
  class CopyProjectFoldersJob < ApplicationJob
    include GoodJob::ActiveJobExtensions::Batches

    retry_on Errors::PollingRequired, attempts: 50, wait: ->(executions) do
      (executions**2) + (Kernel.rand * (executions**2) * 0.15) + 2
    end
    discard_on HTTPX::HTTPError

    def perform(source:, target:, work_packages_map:)
      @source = source
      user = batch.properties[:user]

      project_folder_result = results_from_polling || initiate_copy(target)

      ProjectStorages::UpdateService.new(user:, model: target)
                                    .call(project_folder_id: project_folder_result.result.id,
                                          project_folder_mode: source.project_folder_mode)
                                    .on_failure { |failed| log_failure(failed) and return failed }

      FileLinks::CopyFileLinksService.call(source: @source, target:, user:, work_packages_map:)
    end

    private

    def initiate_copy(target)
      ProjectStorages::CopyProjectFoldersService
        .call(source: @source, target:)
        .on_success { |success| prepare_polling(success.result) }
    end

    def prepare_polling(result)
      return unless result.requires_polling?

      batch.properties[:polling] ||= {}
      batch.properties[:polling][@source.id.to_s] = { polling_state: :ongoing, polling_url: result.polling_url }
      batch.save

      raise Errors::PollingRequired, "Storage #{@source.storage.name} requires polling"
    end

    def polling?
      polling_info[:polling_url] == :ongoing
    end

    def results_from_polling
      return unless polling_info

      response = OpenProject.httpx.get(polling_info[:polling_url]).json(symbolize_keys: true)

      if response[:status] != "completed"
        polling_info[:polling_state] == :ongoing
        batch.save

        raise(Errors::PollingRequired, "#{job_id} Polling not completed yet")
      end

      polling_info[:polling_state] == :completed
      batch.save

      result = Peripherals::StorageInteraction::ResultData::CopyTemplateFolder.new(response[:resourceId], nil, false)
      ServiceResult.success(result:)
    end

    def log_failure(failed)
      return if failed.success?

      OpenProject.logger.warn failed.errors.inspect.to_s
    end

    def polling_info
      batch.properties.dig(:polling, @source.id.to_s)
    end
  end
end