Browse Source

Allow downloading CSVs

pull/2785/head
Kat 7 months ago
parent
commit
42c8fd30da
  1. 18
      app/controllers/csv_downloads_controller.rb
  2. 12
      app/policies/csv_download_policy.rb
  3. 50
      app/services/csv/downloader.rb
  4. 6
      config/routes.rb
  5. 8
      spec/factories/csv_download.rb
  6. 68
      spec/requests/csv_downloads_controller_spec.rb

18
app/controllers/csv_downloads_controller.rb

@ -0,0 +1,18 @@
class CsvDownloadsController < ApplicationController
before_action :authenticate_user!
def download
csv_download = CsvDownload.find(params[:id])
authorize csv_download
downloader = Csv::Downloader.new(csv_download:)
if Rails.env.development?
downloader.call
send_file downloader.path, filename: csv_download.filename, type: "text/csv"
else
presigned_url = downloader.presigned_url
redirect_to presigned_url, allow_other_host: true
end
end
end

12
app/policies/csv_download_policy.rb

@ -0,0 +1,12 @@
class CsvDownloadPolicy
attr_reader :current_user, :csv_download
def initialize(current_user, csv_download)
@current_user = current_user
@csv_download = csv_download
end
def download?
@current_user == @csv_download.user || @current_user.support? || @current_user.organisation == @csv_download.organisation
end
end

50
app/services/csv/downloader.rb

@ -0,0 +1,50 @@
class Csv::Downloader
attr_reader :csv_download
delegate :path, to: :file
def initialize(csv_download:)
@csv_download = csv_download
end
def call
download
end
def delete_local_file!
file.unlink
end
def presigned_url
s3_storage_service.get_presigned_url(csv_download.filename, 60, response_content_disposition: "attachment; filename=#{csv_download.filename}")
end
private
def download
io = storage_service.get_file_io(csv_download.filename)
file.write(io.read)
io.close
file.close
end
def file
@file ||= Tempfile.new
end
def storage_service
@storage_service ||= if FeatureToggle.upload_enabled?
s3_storage_service
else
local_disk_storage_service
end
end
def s3_storage_service
Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["BULK_UPLOAD_BUCKET"])
end
def local_disk_storage_service
Storage::LocalDiskService.new
end
end

6
config/routes.rb

@ -382,6 +382,12 @@ Rails.application.routes.draw do
end
end
resources :csv_downloads, path: "csv-downloads" do
member do
get "download", to: "csv_downloads#download"
end
end
scope via: :all do
match "/404", to: "errors#not_found"
match "/429", to: "errors#too_many_requests", status: 429

8
spec/factories/csv_download.rb

@ -0,0 +1,8 @@
FactoryBot.define do
factory :csv_download do
download_type { "lettings" }
user { create(:user) }
organisation { user.organisation }
filename { "lettings.csv" }
end
end

68
spec/requests/csv_downloads_controller_spec.rb

@ -0,0 +1,68 @@
require "rails_helper"
RSpec.describe CsvDownloadsController, type: :request do
describe "GET #download" do
let(:csv_user) { create(:user) }
let(:csv_download) { create(:csv_download, user: csv_user, organisation: csv_user.organisation) }
let(:get_file_io) do
io = StringIO.new
io.write("hello")
io.rewind
io
end
let(:mock_storage_service) { instance_double(Storage::S3Service, get_file_io:, get_presigned_url: "https://example.com") }
before do
allow(Storage::S3Service).to receive(:new).and_return(mock_storage_service)
end
context "when user is not signed in" do
it "redirects to sign in page" do
get "/csv-downloads/#{csv_download.id}/download"
expect(response).to redirect_to("/account/sign-in")
end
end
context "when user is signed in" do
before do
sign_in user
end
context "and the user is from a different organisation" do
let(:user) { create(:user) }
before do
get "/csv-downloads/#{csv_download.id}/download"
end
it "returns page not found" do
expect(response).to have_http_status(:unauthorized)
end
end
context "and is the user who generated the csv" do
let(:user) { csv_user }
before do
get "/csv-downloads/#{csv_download.id}/download"
end
it "allows downloading the csv" do
expect(response).to have_http_status(:found)
end
end
context "and is the user is from the same organisation" do
let(:user) { create(:user, organisation: csv_user.organisation) }
before do
get "/csv-downloads/#{csv_download.id}/download"
end
it "allows downloading the csv" do
expect(response).to have_http_status(:found)
end
end
end
end
end
Loading…
Cancel
Save