feat(exports): implement admin export
This commit is contained in:
parent
18eb241e1a
commit
87af7f3261
14 changed files with 218 additions and 12 deletions
|
@ -1,9 +1,10 @@
|
|||
class Dossiers::ExportComponent < ApplicationComponent
|
||||
def initialize(procedure:, exports:, statut: nil, count: nil)
|
||||
def initialize(procedure:, exports:, statut: nil, count: nil, export_url: nil)
|
||||
@procedure = procedure
|
||||
@exports = exports
|
||||
@statut = statut
|
||||
@count = count
|
||||
@export_url = export_url
|
||||
end
|
||||
|
||||
def exports
|
||||
|
@ -11,7 +12,7 @@ class Dossiers::ExportComponent < ApplicationComponent
|
|||
end
|
||||
|
||||
def download_export_path(export_format:, force_export: false, no_progress_notification: nil)
|
||||
download_export_instructeur_procedure_path(@procedure,
|
||||
@export_url.call(@procedure,
|
||||
export_format: export_format,
|
||||
statut: @statut,
|
||||
force_export: force_export,
|
||||
|
|
|
@ -4,6 +4,7 @@ module Administrateurs
|
|||
helper_method :create_archive_url
|
||||
|
||||
def index
|
||||
@exports = Export.find_for_groupe_instructeurs(all_groupe_instructeurs.map(&:id), nil)
|
||||
@average_dossier_weight = @procedure.average_dossier_weight
|
||||
@count_dossiers_termines_by_month = Traitement.count_dossiers_termines_by_month(all_groupe_instructeurs)
|
||||
@archives = Archive.for_groupe_instructeur(all_groupe_instructeurs).to_a
|
||||
|
|
70
app/controllers/administrateurs/exports_controller.rb
Normal file
70
app/controllers/administrateurs/exports_controller.rb
Normal file
|
@ -0,0 +1,70 @@
|
|||
module Administrateurs
|
||||
class ExportsController < AdministrateurController
|
||||
before_action :retrieve_procedure, only: [:download]
|
||||
|
||||
def download
|
||||
export = Export.find_or_create_export(export_format, all_groupe_instructeurs, **export_options)
|
||||
|
||||
if export.ready? && export.old? && force_export?
|
||||
export.destroy
|
||||
export = Export.find_or_create_export(export_format, all_groupe_instructeurs, **export_options)
|
||||
end
|
||||
|
||||
if export.ready?
|
||||
respond_to do |format|
|
||||
format.turbo_stream do
|
||||
@dossiers_count = export.count
|
||||
assign_exports
|
||||
flash.notice = "L’export au format \"#{export_format}\" est prêt. Vous pouvez le <a href=\"#{export.file.service_url}\">télécharger</a>"
|
||||
end
|
||||
|
||||
format.html do
|
||||
redirect_to export.file.service_url
|
||||
end
|
||||
end
|
||||
else
|
||||
respond_to do |format|
|
||||
notice_message = "Nous générons cet export. Veuillez revenir dans quelques minutes pour le télécharger."
|
||||
|
||||
format.turbo_stream do
|
||||
@dossiers_count = export.count
|
||||
assign_exports
|
||||
if !params[:no_progress_notification]
|
||||
flash.notice = notice_message
|
||||
end
|
||||
end
|
||||
|
||||
format.html do
|
||||
redirect_to admin_procedure_archives_url(@procedure), notice: notice_message
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def export_format
|
||||
@export_format ||= params[:export_format]
|
||||
end
|
||||
|
||||
def force_export?
|
||||
@force_export ||= params[:force_export].present?
|
||||
end
|
||||
|
||||
def export_options
|
||||
@export_options ||= {
|
||||
time_span_type: params[:time_span_type],
|
||||
statut: params[:statut],
|
||||
procedure_presentation: nil
|
||||
}.compact
|
||||
end
|
||||
|
||||
def all_groupe_instructeurs
|
||||
@procedure.groupe_instructeurs
|
||||
end
|
||||
|
||||
def assign_exports
|
||||
@exports = Export.find_for_groupe_instructeurs(all_groupe_instructeurs.map(&:id), nil)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,6 +7,7 @@
|
|||
.container
|
||||
%h1.mb-2
|
||||
Archives
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, export_url: method(:download_admin_procedure_exports_path))
|
||||
|
||||
= render partial: "shared/archives/notice"
|
||||
= render partial: "shared/archives/table", locals: {count_dossiers_termines_by_month: @count_dossiers_termines_by_month, archives: @archives, average_dossier_weight: @average_dossier_weight, procedure: @procedure }
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
- if @can_download_dossiers
|
||||
= turbo_stream.update_all '.procedure-actions' do
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, count: @dossiers_count, export_url: method(:admin_procedure_exports_path))
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
.procedure-actions
|
||||
- if @can_download_dossiers
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports)
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, export_url: method(:download_export_instructeur_procedure_path))
|
||||
|
||||
.container.flex= render partial: "tabs", locals: { procedure: @procedure,
|
||||
statut: @statut,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- if @can_download_dossiers
|
||||
- if @statut.nil?
|
||||
= turbo_stream.update_all '.procedure-actions' do
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports)
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, export_url: method(:download_export_instructeur_procedure_path))
|
||||
- else
|
||||
= turbo_stream.update_all '.dossiers-export' do
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, statut: @statut, count: @dossiers_count)
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, statut: @statut, count: @dossiers_count, export_url: method(:download_export_instructeur_procedure_path))
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
.procedure-actions
|
||||
- if @can_download_dossiers
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports)
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, export_url: method(:download_export_instructeur_procedure_path))
|
||||
|
||||
.container.flex= render partial: "tabs", locals: { procedure: @procedure,
|
||||
statut: @statut,
|
||||
|
@ -65,7 +65,7 @@
|
|||
= render partial: "dossiers_filter", locals: { procedure: @procedure, procedure_presentation: @procedure_presentation, current_filters: @current_filters, statut: @statut, filterable_fields_for_select: @filterable_fields_for_select }
|
||||
- if @dossiers_count > 0
|
||||
.dossiers-export
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, statut: @statut, count: @dossiers_count)
|
||||
= render Dossiers::ExportComponent.new(procedure: @procedure, exports: @exports, statut: @statut, count: @dossiers_count, export_url: method(:download_export_instructeur_procedure_path))
|
||||
|
||||
%table.table.dossiers-table.hoverable
|
||||
%thead
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
{
|
||||
"ignored_warnings": [
|
||||
{
|
||||
"warning_type": "Redirect",
|
||||
"warning_code": 18,
|
||||
"fingerprint": "170b506bd3d25eb50f464703f9d993bf2e124d5cbc8478ce9d9d06a15b4bc55e",
|
||||
"check_name": "Redirect",
|
||||
"message": "Possible unprotected redirect",
|
||||
"file": "app/controllers/instructeurs/exports_controller.rb",
|
||||
"line": 26,
|
||||
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
|
||||
"code": "redirect_to(Export.find_or_create_export(export_format, groupe_instructeurs, **export_options).file.service_url)",
|
||||
"render_path": null,
|
||||
"location": {
|
||||
"type": "method",
|
||||
"class": "Instructeurs::ExportsController",
|
||||
"method": "download"
|
||||
},
|
||||
"user_input": "Export.find_or_create_export(export_format, groupe_instructeurs, **export_options).file.service_url",
|
||||
"confidence": "High",
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"warning_type": "Cross-Site Scripting",
|
||||
"warning_code": 2,
|
||||
|
@ -112,6 +132,26 @@
|
|||
"confidence": "High",
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"warning_type": "Redirect",
|
||||
"warning_code": 18,
|
||||
"fingerprint": "8f8133f0679f6301e098c4b9c61c2217224126856963abd02cdc5e54efb4e818",
|
||||
"check_name": "Redirect",
|
||||
"message": "Possible unprotected redirect",
|
||||
"file": "app/controllers/administrateurs/exports_controller.rb",
|
||||
"line": 22,
|
||||
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
|
||||
"code": "redirect_to(Export.find_or_create_export(export_format, all_groupe_instructeurs, **export_options).file.service_url)",
|
||||
"render_path": null,
|
||||
"location": {
|
||||
"type": "method",
|
||||
"class": "Administrateurs::ExportsController",
|
||||
"method": "download"
|
||||
},
|
||||
"user_input": "Export.find_or_create_export(export_format, all_groupe_instructeurs, **export_options).file.service_url",
|
||||
"confidence": "High",
|
||||
"note": ""
|
||||
},
|
||||
{
|
||||
"warning_type": "SQL Injection",
|
||||
"warning_code": 0,
|
||||
|
@ -153,6 +193,6 @@
|
|||
"note": ""
|
||||
}
|
||||
],
|
||||
"updated": "2022-06-15 16:15:28 +0200",
|
||||
"updated": "2022-07-08 13:14:19 +0200",
|
||||
"brakeman_version": "5.2.2"
|
||||
}
|
||||
|
|
7
config/initializers/routing.rb
Normal file
7
config/initializers/routing.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class CustomRoutes
|
||||
def self.load
|
||||
Rails.application.routes.draw do
|
||||
get "/provider_url" => redirect("https://numerique.gouv.fr/"), as: :provider
|
||||
end
|
||||
end
|
||||
end
|
|
@ -339,6 +339,8 @@ Rails.application.routes.draw do
|
|||
scope module: 'instructeurs', as: 'instructeur' do
|
||||
resources :procedures, only: [:index, :show], param: :procedure_id do
|
||||
member do
|
||||
resources :archives, only: [:index, :create]
|
||||
|
||||
resources :groupes, only: [:index, :show], controller: 'groupe_instructeurs' do
|
||||
member do
|
||||
post 'add_instructeur'
|
||||
|
@ -396,8 +398,6 @@ Rails.application.routes.draw do
|
|||
get 'telecharger_pjs' => 'dossiers#telecharger_pjs'
|
||||
end
|
||||
end
|
||||
|
||||
resources :archives, only: [:index, :create]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -409,6 +409,13 @@ Rails.application.routes.draw do
|
|||
scope module: 'administrateurs', path: 'admin', as: 'admin' do
|
||||
resources :procedures do
|
||||
resources :archives, only: [:index, :create]
|
||||
resources :exports, only: [] do
|
||||
collection do
|
||||
get 'download'
|
||||
post 'download'
|
||||
end
|
||||
end
|
||||
|
||||
collection do
|
||||
get 'new_from_existing'
|
||||
end
|
||||
|
|
67
spec/controllers/administrateurs/exports_controller_spec.rb
Normal file
67
spec/controllers/administrateurs/exports_controller_spec.rb
Normal file
|
@ -0,0 +1,67 @@
|
|||
describe Administrateurs::ExportsController, type: :controller do
|
||||
describe '#download' do
|
||||
let(:administrateur) { create(:administrateur) }
|
||||
before { sign_in(administrateur.user) }
|
||||
|
||||
subject do
|
||||
get :download, params: { export_format: :csv, procedure_id: procedure.id }
|
||||
end
|
||||
|
||||
context 'when the procedure does not belongs to admin' do
|
||||
let!(:procedure) { create(:procedure, administrateurs: [create(:administrateur)]) }
|
||||
it 'blocks' do
|
||||
is_expected.to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when admin is allowed' do
|
||||
let!(:procedure) { create(:procedure, administrateurs: [administrateur]) }
|
||||
|
||||
context 'when the export is does not exist' do
|
||||
it 'displays an notice' do
|
||||
is_expected.to redirect_to(admin_procedure_archives_url(procedure))
|
||||
expect(flash.notice).to be_present
|
||||
end
|
||||
|
||||
it { expect { subject }.to change(Export, :count).by(1) }
|
||||
end
|
||||
|
||||
context 'when the export is not ready' do
|
||||
before do
|
||||
create(:export, groupe_instructeurs: procedure.groupe_instructeurs)
|
||||
end
|
||||
|
||||
it 'displays an notice' do
|
||||
is_expected.to redirect_to(admin_procedure_archives_url(procedure))
|
||||
expect(flash.notice).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the export is ready' do
|
||||
let(:export) { create(:export, groupe_instructeurs: procedure.groupe_instructeurs) }
|
||||
|
||||
before do
|
||||
export.file.attach(io: StringIO.new('export'), filename: 'file.csv')
|
||||
end
|
||||
|
||||
it 'displays the download link' do
|
||||
subject
|
||||
expect(response.headers['Location']).to start_with("http://test.host/rails/active_storage/disk")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the turbo_stream format is used' do
|
||||
before do
|
||||
post :download,
|
||||
params: { export_format: :csv, procedure_id: procedure.id },
|
||||
format: :turbo_stream
|
||||
end
|
||||
|
||||
it 'responds in the correct format' do
|
||||
expect(response.media_type).to eq('text/vnd.turbo-stream.html')
|
||||
expect(response).to have_http_status(:ok)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -32,5 +32,13 @@ describe 'Creating a new procedure', js: true do
|
|||
page.first(".archive-table .button").click
|
||||
}.to have_enqueued_job(ArchiveCreationJob).with(procedure, an_instance_of(Archive), administrateur)
|
||||
expect(page).to have_content("Votre demande a été prise en compte. Selon le nombre de dossiers, cela peut prendre de quelques minutes a plusieurs heures. Vous recevrez un courriel lorsque le fichier sera disponible.")
|
||||
|
||||
# check exports
|
||||
click_on "Télécharger tous les dossiers"
|
||||
|
||||
expect {
|
||||
click_on "Demander un export au format .xlsx"
|
||||
expect(page).to have_content("Nous générons cet export. Veuillez revenir dans quelques minutes pour le télécharger.")
|
||||
}.to have_enqueued_job(ExportJob).with(an_instance_of(Export))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -120,9 +120,10 @@ describe 'Instructing a dossier:', js: true do
|
|||
within(:css, '.dossiers-export') do
|
||||
click_on "Demander un export au format .csv"
|
||||
end
|
||||
expect(page).to have_text('Nous générons cet export.')
|
||||
expect(page).to have_text('Un export au format .csv est en train d’être généré')
|
||||
|
||||
expect(page).to have_text('Nous générons cet export.')
|
||||
click_on "Télécharger un dossier"
|
||||
expect(page).to have_text('Un export au format .csv est en train d’être généré')
|
||||
perform_enqueued_jobs(only: ExportJob)
|
||||
assert_performed_jobs 2
|
||||
page.driver.browser.navigate.refresh
|
||||
|
|
Loading…
Reference in a new issue