FIX: use hijack for processing bulk invites (#7679)
FIX: do not store bulk invite CSV file on server
This commit is contained in:
parent
ecc9c76692
commit
e7fe7010b8
|
@ -172,24 +172,31 @@ class InvitesController < ApplicationController
|
|||
def upload_csv
|
||||
guardian.ensure_can_bulk_invite_to_forum!(current_user)
|
||||
|
||||
file = params[:file] || params[:files].first
|
||||
name = params[:name] || File.basename(file.original_filename, ".*")
|
||||
extension = File.extname(file.original_filename)
|
||||
hijack do
|
||||
begin
|
||||
file = params[:file] || params[:files].first
|
||||
|
||||
begin
|
||||
data = if extension.downcase == ".csv"
|
||||
path = Invite.create_csv(file, name)
|
||||
Jobs.enqueue(:bulk_invite, filename: "#{name}#{extension}", current_user_id: current_user.id)
|
||||
{ url: path }
|
||||
else
|
||||
failed_json.merge(errors: [I18n.t("bulk_invite.file_should_be_csv")])
|
||||
if File.read(file.tempfile).scan(/\n/).count.to_i > 50000
|
||||
return render json: failed_json.merge(errors: [I18n.t("bulk_invite.max_rows")]), status: 422
|
||||
end
|
||||
|
||||
invites = []
|
||||
CSV.foreach(file.tempfile) do |row|
|
||||
invite_hash = { email: row[0], groups: row[1], topic_id: row[2] }
|
||||
if invite_hash[:email].present?
|
||||
invites.push(invite_hash)
|
||||
end
|
||||
end
|
||||
if invites.present?
|
||||
Jobs.enqueue(:bulk_invite, invites: invites, current_user_id: current_user.id)
|
||||
render json: success_json
|
||||
else
|
||||
render json: failed_json.merge(errors: [I18n.t("bulk_invite.error")]), status: 422
|
||||
end
|
||||
rescue
|
||||
render json: failed_json.merge(errors: [I18n.t("bulk_invite.error")]), status: 422
|
||||
end
|
||||
rescue
|
||||
failed_json.merge(errors: [I18n.t("bulk_invite.error")])
|
||||
end
|
||||
MessageBus.publish("/uploads/csv", data.as_json, user_ids: [current_user.id])
|
||||
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
def fetch_username
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
require_dependency 'system_message'
|
||||
|
||||
module Jobs
|
||||
|
@ -18,48 +17,38 @@ module Jobs
|
|||
end
|
||||
|
||||
def execute(args)
|
||||
filename = args[:filename]
|
||||
raise Discourse::InvalidParameters.new(:filename) if filename.blank?
|
||||
invites = args[:invites]
|
||||
raise Discourse::InvalidParameters.new(:invites) if invites.blank?
|
||||
|
||||
@current_user = User.find_by(id: args[:current_user_id])
|
||||
raise Discourse::InvalidParameters.new(:current_user_id) unless @current_user
|
||||
@guardian = Guardian.new(@current_user)
|
||||
|
||||
csv_path = "#{Invite.base_directory}/#{filename}"
|
||||
|
||||
# read csv file, and send out invitations
|
||||
read_csv_file(csv_path)
|
||||
process_invites(invites)
|
||||
ensure
|
||||
# send notification to user regarding progress
|
||||
notify_user
|
||||
File.delete(csv_path) if csv_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def read_csv_file(csv_path)
|
||||
file = File.open(csv_path, encoding: 'bom|utf-8')
|
||||
CSV.new(file).each do |csv_info|
|
||||
if csv_info[0]
|
||||
if (EmailValidator.email_regex =~ csv_info[0])
|
||||
# email is valid
|
||||
send_invite(csv_info, $INPUT_LINE_NUMBER)
|
||||
@sent += 1
|
||||
else
|
||||
# invalid email
|
||||
save_log "Invalid Email '#{csv_info[0]}' at line number '#{$INPUT_LINE_NUMBER}'"
|
||||
@failed += 1
|
||||
end
|
||||
def process_invites(invites)
|
||||
invites.each do |invite|
|
||||
if (EmailValidator.email_regex =~ invite[:email])
|
||||
# email is valid
|
||||
send_invite(invite)
|
||||
@sent += 1
|
||||
else
|
||||
# invalid email
|
||||
save_log "Invalid Email '#{invite[:email]}"
|
||||
@failed += 1
|
||||
end
|
||||
end
|
||||
rescue Exception => e
|
||||
save_log "Bulk Invite Process Failed -- '#{e.message}'"
|
||||
@failed += 1
|
||||
ensure
|
||||
file&.close
|
||||
end
|
||||
|
||||
def get_groups(group_names, csv_line_number)
|
||||
def get_groups(group_names)
|
||||
groups = []
|
||||
|
||||
if group_names
|
||||
|
@ -73,7 +62,7 @@ module Jobs
|
|||
groups.push(group)
|
||||
else
|
||||
# invalid group
|
||||
save_log "Invalid Group '#{group_name}' at line number '#{csv_line_number}'"
|
||||
save_log "Invalid Group '#{group_name}'"
|
||||
@failed += 1
|
||||
end
|
||||
}
|
||||
|
@ -82,13 +71,13 @@ module Jobs
|
|||
groups
|
||||
end
|
||||
|
||||
def get_topic(topic_id, csv_line_number)
|
||||
def get_topic(topic_id)
|
||||
topic = nil
|
||||
|
||||
if topic_id
|
||||
topic = Topic.find_by_id(topic_id)
|
||||
if topic.nil?
|
||||
save_log "Invalid Topic ID '#{topic_id}' at line number '#{csv_line_number}'"
|
||||
save_log "Invalid Topic ID '#{topic_id}'"
|
||||
@failed += 1
|
||||
end
|
||||
end
|
||||
|
@ -96,10 +85,10 @@ module Jobs
|
|||
return topic
|
||||
end
|
||||
|
||||
def send_invite(csv_info, csv_line_number)
|
||||
email = csv_info[0]
|
||||
groups = get_groups(csv_info[1], csv_line_number)
|
||||
topic = get_topic(csv_info[2], csv_line_number)
|
||||
def send_invite(invite)
|
||||
email = invite[:email]
|
||||
groups = get_groups(invite[:groups])
|
||||
topic = get_topic(invite[:topic_id])
|
||||
|
||||
begin
|
||||
if user = User.find_by_email(email)
|
||||
|
@ -171,7 +160,5 @@ module Jobs
|
|||
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -242,14 +242,6 @@ class Invite < ActiveRecord::Base
|
|||
def self.base_directory
|
||||
File.join(Rails.root, "public", "uploads", "csv", RailsMultisite::ConnectionManagement.current_db)
|
||||
end
|
||||
|
||||
def self.create_csv(file, name)
|
||||
extension = File.extname(file.original_filename)
|
||||
path = "#{Invite.base_directory}/#{name}#{extension}"
|
||||
FileUtils.mkdir_p(Pathname.new(path).dirname)
|
||||
File.open(path, "wb") { |f| f << file.tempfile.read }
|
||||
path
|
||||
end
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
|
|
|
@ -218,6 +218,7 @@ en:
|
|||
|
||||
bulk_invite:
|
||||
file_should_be_csv: "The uploaded file should be of csv format."
|
||||
max_rows: "Maximum of 50,000 invites can be sent at a time. Try splitting the file in smaller parts."
|
||||
error: "There was an error uploading that file. Please try again later."
|
||||
|
||||
topic_invite:
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
test2@discourse.org
|
||||
test@discourse.org,GROUP1;group2,999
|
|
|
@ -10,30 +10,22 @@ describe Jobs::BulkInvite do
|
|||
fab!(:group2) { Fabricate(:group, name: 'group2') }
|
||||
fab!(:topic) { Fabricate(:topic, id: 999) }
|
||||
let(:email) { "test@discourse.org" }
|
||||
let(:basename) { "bulk_invite.csv" }
|
||||
let(:filename) { "#{Invite.base_directory}/#{basename}" }
|
||||
let(:invites) { [{ email: 'test2@discourse.org' }, { email: 'test@discourse.org', groups: 'GROUP1;group2', topic_id: 999 }] }
|
||||
|
||||
before do
|
||||
Invite.create_csv(
|
||||
fixture_file_upload("#{Rails.root}/spec/fixtures/csv/#{basename}"),
|
||||
"bulk_invite"
|
||||
)
|
||||
end
|
||||
|
||||
it 'raises an error when the filename is missing' do
|
||||
it 'raises an error when the invites array is missing' do
|
||||
expect { Jobs::BulkInvite.new.execute(current_user_id: user.id) }
|
||||
.to raise_error(Discourse::InvalidParameters, /filename/)
|
||||
.to raise_error(Discourse::InvalidParameters, /invites/)
|
||||
end
|
||||
|
||||
it 'raises an error when current_user_id is not valid' do
|
||||
expect { Jobs::BulkInvite.new.execute(filename: filename) }
|
||||
expect { Jobs::BulkInvite.new.execute(invites: invites) }
|
||||
.to raise_error(Discourse::InvalidParameters, /current_user_id/)
|
||||
end
|
||||
|
||||
it 'creates the right invites' do
|
||||
described_class.new.execute(
|
||||
current_user_id: admin.id,
|
||||
filename: basename,
|
||||
invites: invites
|
||||
)
|
||||
|
||||
invite = Invite.last
|
||||
|
@ -53,7 +45,7 @@ describe Jobs::BulkInvite do
|
|||
|
||||
described_class.new.execute(
|
||||
current_user_id: admin.id,
|
||||
filename: basename,
|
||||
invites: invites
|
||||
)
|
||||
|
||||
invite = Invite.last
|
||||
|
@ -70,7 +62,7 @@ describe Jobs::BulkInvite do
|
|||
|
||||
described_class.new.execute(
|
||||
current_user_id: user.id,
|
||||
filename: basename
|
||||
invites: invites
|
||||
)
|
||||
|
||||
invite = Invite.last
|
||||
|
@ -90,7 +82,7 @@ describe Jobs::BulkInvite do
|
|||
expect do
|
||||
described_class.new.execute(
|
||||
current_user_id: admin.id,
|
||||
filename: basename
|
||||
invites: invites
|
||||
)
|
||||
end.to change { Invite.count }.by(1)
|
||||
|
||||
|
|
Loading…
Reference in New Issue