diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index cd4c3254e..b4854da47 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -10,6 +10,17 @@ class UsersController < ApplicationController redirect_to users_organisation_path(current_user.organisation) unless current_user.support? @pagy, @users = pagy(User.all.where(active: true)) + + respond_to do |format| + format.html + format.csv do + if current_user.support? + send_data User.all.where(active: true).to_csv, filename: "users-#{Time.zone.now}.csv" + else + head :unauthorized + end + end + end end def show; end diff --git a/app/models/user.rb b/app/models/user.rb index 6cb41585b..c458f80cd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -96,4 +96,20 @@ class User < ApplicationRecord %w[status years user] end end + + delegate :name, to: :organisation, prefix: true + + def self.download_attributes + %w[id email name organisation_name role old_user_id is_dpo is_key_contact active sign_in_count last_sign_in_at] + end + + def self.to_csv + CSV.generate(headers: true) do |csv| + csv << download_attributes + + all.find_each do |record| + csv << download_attributes.map { |attr| record.public_send(attr) } + end + end + end end diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 7a24b1838..87e5b37a0 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -12,6 +12,9 @@ <%= @pagy.count %> total users + <% if current_user.support? %> + <%= govuk_link_to "Download (CSV)", "/users.csv", type: "text/csv" %> + <% end %> <% end %> <%= table.head do |head| %> <%= head.row do |row| %> diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb index cb7a5a12f..9380fe624 100644 --- a/spec/requests/users_controller_spec.rb +++ b/spec/requests/users_controller_spec.rb @@ -348,6 +348,24 @@ RSpec.describe UsersController, type: :request do follow_redirect! expect(path).to match("/organisations/#{user.organisation.id}/users") end + + it "does not show the download csv link" do + expect(page).not_to have_link("Download (CSV)", href: "/users.csv") + end + end + + describe "CSV download" do + let(:headers) { { "Accept" => "text/csv" } } + let(:user) { FactoryBot.create(:user) } + + before do + sign_in user + get "/users", headers:, params: {} + end + + it "returns 401 unauthorized" do + expect(response).to have_http_status(:unauthorized) + end end describe "#show" do @@ -728,6 +746,37 @@ RSpec.describe UsersController, type: :request do it "shows the pagination count" do expect(page).to have_content("3 total users") end + + it "shows the download csv link" do + expect(page).to have_link("Download (CSV)", href: "/users.csv") + end + end + + describe "CSV download" do + let(:headers) { { "Accept" => "text/csv" } } + let(:user) { FactoryBot.create(:user, :support) } + + before do + FactoryBot.create_list(:user, 25) + sign_in user + get "/users", headers:, params: {} + end + + it "downloads a CSV file with headers" do + csv = CSV.parse(response.body) + expect(csv.first.second).to eq("email") + expect(csv.second.first).to eq(user.id.to_s) + end + + it "downloads all users" do + csv = CSV.parse(response.body) + expect(csv.count).to eq(27) + end + + it "downloads organisation names rather than ids" do + csv = CSV.parse(response.body) + expect(csv.second[3]).to eq(user.organisation.name.to_s) + end end describe "#show" do