add job to cleanup uncontainered attachments

This commit is contained in:
Jens Ulferts
2018-05-31 08:23:57 +02:00
parent e58a96515c
commit 57c8aeb5aa
6 changed files with 165 additions and 8 deletions
+11
View File
@@ -52,6 +52,9 @@ class Attachment < ActiveRecord::Base
after_commit :extract_fulltext, on: :create
after_create :schedule_cleanup_uncontainered_job,
unless: :containered?
##
# Returns an URL if the attachment is stored in an external (fog) attachment storage
# or nil otherwise.
@@ -121,6 +124,10 @@ class Attachment < ActiveRecord::Base
file.readable?
end
def containered?
container.present?
end
def diskfile
file.local_file
end
@@ -180,6 +187,10 @@ class Attachment < ActiveRecord::Base
private
def schedule_cleanup_uncontainered_job
Delayed::Job::enqueue Attachments::CleanupUncontaineredJob.new
end
def filesize_below_allowed_maximum
if filesize > Setting.attachment_max_size.to_i.kilobytes
errors.add(:file, :file_too_large, count: Setting.attachment_max_size.to_i.kilobytes)
@@ -0,0 +1,48 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
#
# 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-2017 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 doc/COPYRIGHT.rdoc for more details.
#++
class Attachments::CleanupUncontaineredJob < ApplicationJob
def perform
Attachment
.where(container: nil)
.where(too_old)
.destroy_all
end
private
def too_old
attachment_table = Attachment.arel_table
attachment_table[:created_at]
.lteq(Time.now - OpenProject::Configuration.attachments_grace_period.minutes)
.to_sql
end
end
+4
View File
@@ -186,6 +186,10 @@ default:
# attachments_storage_path: /var/openproject/files
# attachments_storage_path:
# Grace period until uploaded but unassigned (i.e. to a container like work packages,
# wiki pages) attachments are deleted (in minutes)
# attachment_grace_period: 180
# Configuration of the autologin cookie.
# autologin_cookie_name: the name of the cookie (default: autologin)
# autologin_cookie_path: the cookie path (default: /)
+9 -8
View File
@@ -39,6 +39,7 @@ module OpenProject
@defaults = {
'attachments_storage' => 'file',
'attachments_storage_path' => nil,
'attachments_grace_period' => 180,
'autologin_cookie_name' => 'autologin',
'autologin_cookie_path' => '/',
'autologin_cookie_secure' => false,
@@ -457,15 +458,15 @@ module OpenProject
def define_config_methods
@config.keys.each do |setting|
(class << self; self; end).class_eval do
define_method setting do
self[setting]
end
next if respond_to? setting
define_method "#{setting}?" do
['true', true, '1'].include? self[setting]
end
end unless respond_to? setting
define_singleton_method setting do
self[setting]
end
define_singleton_method "#{setting}?" do
['true', true, '1'].include? self[setting]
end
end
end
end
+37
View File
@@ -134,6 +134,20 @@ describe Attachment, type: :model do
end
end
describe '#containered?' do
it 'is false if the attachment has no container' do
stubbed_attachment.container = nil
expect(stubbed_attachment)
.not_to be_containered
end
it 'is true if the attachment has a container' do
expect(stubbed_attachment)
.to be_containered
end
end
describe 'create' do
it('creates a jpg file called test') do
expect(File.exists?(attachment.diskfile.path)).to eq true
@@ -160,6 +174,29 @@ describe Attachment, type: :model do
expect(attachment.digest)
.to eql Digest::MD5.file(file.path).hexdigest
end
it 'adds no cleanup job' do
expect(Delayed::Job)
.not_to receive(:enqueue)
.with an_instance_of(Attachments::CleanupUncontaineredJob)
attachment.save!
end
context 'with an unclaimed attachment' do
let(:container) { nil }
it 'adds a cleanup job' do
allow(Delayed::Job)
.to receive(:enqueue)
expect(Delayed::Job)
.to receive(:enqueue)
.with an_instance_of(Attachments::CleanupUncontaineredJob)
attachment.save!
end
end
end
describe 'two attachments with same file name' do
@@ -0,0 +1,56 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
#
# 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-2017 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 doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe Attachments::CleanupUncontaineredJob, type: :job do
let(:grace_period) { 120 }
let!(:containered_attachment) { FactoryBot.create(:attachment) }
let!(:old_uncontainered_attachment) do
FactoryBot.create(:attachment, container: nil, created_at: Time.now - grace_period.minutes)
end
let!(:new_uncontainered_attachment) do
FactoryBot.create(:attachment, container: nil, created_at: Time.now - (grace_period - 1).minutes)
end
let(:job) { described_class.new }
before do
allow(OpenProject::Configuration)
.to receive(:attachments_grace_period)
.and_return(grace_period)
end
it 'removes all uncontainered attachments that are older than the grace period' do
job.perform
expect(Attachment.all)
.to match_array([containered_attachment, new_uncontainered_attachment])
end
end