From 1784980e6c7a65e998fa0268cf5da2828dc60de1 Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Fri, 6 Jan 2017 19:12:53 +0200 Subject: [PATCH 1/6] Add csv export button do domain list in registrar area #248 --- .../registrar/domains_controller.rb | 16 ++++++- app/presenters/domain_presenter.rb | 2 +- .../registrar/domain_list_csv_presenter.rb | 45 +++++++++++++++++++ app/views/registrar/domains/index.haml | 4 ++ config/locales/registrar/domains.en.yml | 10 +++++ .../registrar/domains/csv_export_spec.rb | 13 ++++++ spec/presenters/domain_presenter_spec.rb | 1 + .../domain_list_csv_presenter_spec.rb | 45 +++++++++++++++++++ .../registrar/domains_controller_spec.rb | 28 ++++++++++++ .../routing/registrar/domains_routing_spec.rb | 9 ++++ 10 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 app/presenters/registrar/domain_list_csv_presenter.rb create mode 100644 config/locales/registrar/domains.en.yml create mode 100644 spec/features/registrar/domains/csv_export_spec.rb create mode 100644 spec/presenters/registrar/domain_list_csv_presenter_spec.rb create mode 100644 spec/requests/registrar/domains_controller_spec.rb create mode 100644 spec/routing/registrar/domains_routing_spec.rb diff --git a/app/controllers/registrar/domains_controller.rb b/app/controllers/registrar/domains_controller.rb index a02fb4aa1..560a965f3 100644 --- a/app/controllers/registrar/domains_controller.rb +++ b/app/controllers/registrar/domains_controller.rb @@ -14,7 +14,7 @@ class Registrar::DomainsController < Registrar::DeppController # EPP controller if params[:q].length == 1 && params[:q][:name_matches].present? @domain = Domain.find_by(name: params[:q][:name_matches]) if @domain - redirect_to info_registrar_domains_path(domain_name: @domain.name) and return + redirect_to info_registrar_domains_url(domain_name: @domain.name) and return end end @@ -40,6 +40,20 @@ class Registrar::DomainsController < Registrar::DeppController # EPP controller end @domains = @domains.per(params[:results_per_page]) if params[:results_per_page].to_i > 0 + + respond_to do |format| + format.html + format.csv do + domain_presenters = [] + + @domains.find_each do |domain| + domain_presenters << ::DomainPresenter.new(domain: domain, view: view_context) + end + + csv = Registrar::DomainListCSVPresenter.new(domains: domain_presenters, view: view_context).to_s + send_data(csv) + end + end end # rubocop: enable Metrics/PerceivedComplexity # rubocop: enable Metrics/CyclomaticComplexity diff --git a/app/presenters/domain_presenter.rb b/app/presenters/domain_presenter.rb index 2d0cfd791..266115b91 100644 --- a/app/presenters/domain_presenter.rb +++ b/app/presenters/domain_presenter.rb @@ -1,5 +1,5 @@ class DomainPresenter - delegate :name, :registrant_name, :registrant_id, to: :domain + delegate :name, :registrant_name, :registrant_id, :registrant_code, to: :domain def initialize(domain:, view:) @domain = domain diff --git a/app/presenters/registrar/domain_list_csv_presenter.rb b/app/presenters/registrar/domain_list_csv_presenter.rb new file mode 100644 index 000000000..d6f0c46bd --- /dev/null +++ b/app/presenters/registrar/domain_list_csv_presenter.rb @@ -0,0 +1,45 @@ +class Registrar::DomainListCSVPresenter + def initialize(domains:, view:) + @domains = domains + @view = view + end + + def to_s + table = CSV::Table.new([header]) + + domains.each do |domain| + table << domain_to_row(domain: domain) + end + + table.to_s + end + + private + + def header + columns = %w( + domain_name + registrant_name + registrant_code + expire_time + ) + + columns.map! { |column| view.t("registrar.domains.index.csv.#{column}") } + + CSV::Row.new(columns, [], true) + end + + def domain_to_row(domain:) + row = [] + row[0] = domain.name + row[1] = domain.registrant_name + row[2] = domain.registrant_code + row[3] = domain.expire_date + row + + CSV::Row.new([], row) + end + + attr_reader :domains + attr_reader :view +end diff --git a/app/views/registrar/domains/index.haml b/app/views/registrar/domains/index.haml index 587ef2b5e..45eb564b6 100644 --- a/app/views/registrar/domains/index.haml +++ b/app/views/registrar/domains/index.haml @@ -52,6 +52,10 @@   %button.btn.btn-default.js-reset-form = t(:clear_fields) + .row + .col-md-2 + = button_tag t('.export_csv_btn'), class: 'btn btn-primary export-domains-csv-btn', + formaction: registrar_domains_path(format: 'csv') %hr .row diff --git a/config/locales/registrar/domains.en.yml b/config/locales/registrar/domains.en.yml new file mode 100644 index 000000000..af0115a81 --- /dev/null +++ b/config/locales/registrar/domains.en.yml @@ -0,0 +1,10 @@ +en: + registrar: + domains: + index: + export_csv_btn: Export CSV + csv: + domain_name: Domain + registrant_name: Registrant name + registrant_code: Registrant code + expire_time: Date of expiry diff --git a/spec/features/registrar/domains/csv_export_spec.rb b/spec/features/registrar/domains/csv_export_spec.rb new file mode 100644 index 000000000..4d64c76c4 --- /dev/null +++ b/spec/features/registrar/domains/csv_export_spec.rb @@ -0,0 +1,13 @@ +require 'rails_helper' + +RSpec.feature 'CSV Export' do + background do + Setting.registrar_ip_whitelist_enabled = false + sign_in_to_registrar_area(user: FactoryGirl.create(:api_user)) + end + + scenario 'exports csv' do + visit registrar_domains_url + click_button t('registrar.domains.index.export_csv_btn') + end +end diff --git a/spec/presenters/domain_presenter_spec.rb b/spec/presenters/domain_presenter_spec.rb index 2209c01aa..9813311b9 100644 --- a/spec/presenters/domain_presenter_spec.rb +++ b/spec/presenters/domain_presenter_spec.rb @@ -119,6 +119,7 @@ RSpec.describe DomainPresenter do name registrant_name registrant_id + registrant_code ) domain_delegatable_attributes.each do |attribute_name| diff --git a/spec/presenters/registrar/domain_list_csv_presenter_spec.rb b/spec/presenters/registrar/domain_list_csv_presenter_spec.rb new file mode 100644 index 000000000..df4eec1fe --- /dev/null +++ b/spec/presenters/registrar/domain_list_csv_presenter_spec.rb @@ -0,0 +1,45 @@ +require 'rails_helper' + +RSpec.describe Registrar::DomainListCSVPresenter do + let(:domain) { instance_spy(DomainPresenter) } + let(:csv) { CSV.parse(described_class.new(domains: [domain], view: view).to_s, converters: :all) } + + describe 'header' do + subject(:header) { csv.first } + + it 'is present' do + columns = [] + columns[0] = 'Domain' + columns[1] = 'Registrant name' + columns[2] = 'Registrant code' + columns[3] = 'Date of expiry' + columns + + expect(header).to eq(columns) + end + end + + describe 'row' do + subject(:row) { csv.second } + + it 'has domain name' do + expect(domain).to receive(:name).and_return('test name') + expect(row[0]).to eq('test name') + end + + it 'has registrant name' do + expect(domain).to receive(:registrant_name).and_return('test registrant name') + expect(row[1]).to eq('test registrant name') + end + + it 'has registrant code' do + expect(domain).to receive(:registrant_code).and_return('test registrant code') + expect(row[2]).to eq('test registrant code') + end + + it 'has expire date' do + expect(domain).to receive(:expire_date).and_return('expire date') + expect(row[3]).to eq('expire date') + end + end +end diff --git a/spec/requests/registrar/domains_controller_spec.rb b/spec/requests/registrar/domains_controller_spec.rb new file mode 100644 index 000000000..630703c6b --- /dev/null +++ b/spec/requests/registrar/domains_controller_spec.rb @@ -0,0 +1,28 @@ +require 'rails_helper' + +RSpec.describe Registrar::DomainsController, db: true do + describe '#index' do + before do + sign_in_to_registrar_area + end + + it 'responds with success' do + csv_presenter = instance_double(Registrar::DomainListCSVPresenter, to_s: 'csv') + expect(Registrar::DomainListCSVPresenter).to receive(:new).and_return(csv_presenter) + + get registrar_domains_url(format: 'csv') + + expect(response.body).to eq('csv') + end + + it 'returns csv' do + get registrar_domains_url(format: 'csv') + + expect(response).to have_http_status(:success) + end + end + + def sign_in_to_registrar_area(user: FactoryGirl.create(:api_user)) + post registrar_sessions_path, { depp_user: { tag: user.username, password: user.password } } + end +end diff --git a/spec/routing/registrar/domains_routing_spec.rb b/spec/routing/registrar/domains_routing_spec.rb new file mode 100644 index 000000000..e30d1dd24 --- /dev/null +++ b/spec/routing/registrar/domains_routing_spec.rb @@ -0,0 +1,9 @@ +require 'rails_helper' + +RSpec.describe Registrar::DomainsController do + describe 'routing' do + it 'routes to #index' do + expect(get: '/registrar/domains').to route_to('registrar/domains#index') + end + end +end From fd58024226c7be26014a84d76ccccccf3ae8d367 Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Sun, 8 Jan 2017 21:28:21 +0200 Subject: [PATCH 2/6] Introduce ApiUser.min_password_length #248 --- app/models/api_user.rb | 6 +++++- spec/models/api_user_spec.rb | 10 ++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/models/api_user.rb b/app/models/api_user.rb index 5d6e3afb7..6928a933f 100644 --- a/app/models/api_user.rb +++ b/app/models/api_user.rb @@ -11,12 +11,16 @@ class ApiUser < User } end + def self.min_password_length # Must precede .validates + 6 + end + # TODO: should have max request limit per day? belongs_to :registrar has_many :certificates validates :username, :password, :registrar, :roles, presence: true - validates :password, length: { minimum: 6 } + validates :password, length: { minimum: min_password_length } validates :username, uniqueness: true # TODO: probably cache, because it's requested on every EPP diff --git a/spec/models/api_user_spec.rb b/spec/models/api_user_spec.rb index 4b8fd9c4e..20ac4afff 100644 --- a/spec/models/api_user_spec.rb +++ b/spec/models/api_user_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe ApiUser do +RSpec.describe ApiUser do context 'class methods' do before do Fabricate(:api_user, identity_code: '') @@ -26,7 +26,7 @@ describe ApiUser do @api_user.valid? @api_user.errors.full_messages.should match_array([ "Password Password is missing", - "Password is too short (minimum is 6 characters)", + "Password is too short (minimum is #{ApiUser.min_password_length} characters)", "Registrar Registrar is missing", "Username Username is missing", "Roles is missing" @@ -68,4 +68,10 @@ describe ApiUser do end end end + + describe '::min_password_length', db: false do + it 'returns minimum password length' do + expect(described_class.min_password_length).to eq(6) + end + end end From 596cb8f55d422571f6d0ee0f37ebe98086fd8b19 Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Sun, 8 Jan 2017 21:30:01 +0200 Subject: [PATCH 3/6] Introduce ApiUser.min_password_length #248 --- spec/factories/api_user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/factories/api_user.rb b/spec/factories/api_user.rb index 723a966a5..b1aa770c4 100644 --- a/spec/factories/api_user.rb +++ b/spec/factories/api_user.rb @@ -1,7 +1,7 @@ FactoryGirl.define do factory :api_user do sequence(:username) { |n| "test#{n}" } - password 'a' * 6 + password 'a' * ApiUser.min_password_length roles ['super'] registrar From a1bdfa8df41ff07918e705c5ef377aaf25411f40 Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Sun, 8 Jan 2017 21:45:47 +0200 Subject: [PATCH 4/6] Add "registrar_with_unlimited_balance" factory #248 --- spec/factories/api_user.rb | 6 ++++++ spec/factories/registrar.rb | 6 ++++++ spec/features/registrar/domains/csv_export_spec.rb | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/spec/factories/api_user.rb b/spec/factories/api_user.rb index b1aa770c4..01c2c70e0 100644 --- a/spec/factories/api_user.rb +++ b/spec/factories/api_user.rb @@ -8,5 +8,11 @@ FactoryGirl.define do factory :api_user_epp do roles %w(epp static_registrant) end + + factory :api_user_with_unlimited_balance do + after :build do |api_user| + api_user.registrar = create(:registrar_with_unlimited_balance) + end + end end end diff --git a/spec/factories/registrar.rb b/spec/factories/registrar.rb index dd581a1d1..f8c3af81c 100644 --- a/spec/factories/registrar.rb +++ b/spec/factories/registrar.rb @@ -9,5 +9,11 @@ FactoryGirl.define do zip 'test' email 'test@test.com' country_code 'EE' + + factory :registrar_with_unlimited_balance do + after :create do |registrar| + create(:account, registrar: registrar, balance: 1_000_000) + end + end end end diff --git a/spec/features/registrar/domains/csv_export_spec.rb b/spec/features/registrar/domains/csv_export_spec.rb index 4d64c76c4..50fb5eb9a 100644 --- a/spec/features/registrar/domains/csv_export_spec.rb +++ b/spec/features/registrar/domains/csv_export_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.feature 'CSV Export' do background do Setting.registrar_ip_whitelist_enabled = false - sign_in_to_registrar_area(user: FactoryGirl.create(:api_user)) + sign_in_to_registrar_area(user: create(:api_user_with_unlimited_balance)) end scenario 'exports csv' do From 1c3a482304d0df5382fbdac8fbdd7bb374c6ad0e Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Tue, 24 Jan 2017 04:10:40 +0200 Subject: [PATCH 5/6] Add polls stub for testing in registrar area #248 --- app/controllers/registrar/polls_controller.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/controllers/registrar/polls_controller.rb b/app/controllers/registrar/polls_controller.rb index e29f02f67..5f893e3c3 100644 --- a/app/controllers/registrar/polls_controller.rb +++ b/app/controllers/registrar/polls_controller.rb @@ -3,7 +3,12 @@ class Registrar::PollsController < Registrar::DeppController # EPP controller before_action :init_epp_xml def show - @data = depp_current_user.request(@ex.poll) + if Rails.env.test? # Stub for depp server request + @data = Object.new + def @data.css(key); []; end + else + @data = depp_current_user.request(@ex.poll) + end end def destroy From 019ea5eab63d63aa0d87100d6d526be471c668c6 Mon Sep 17 00:00:00 2001 From: Artur Beljajev Date: Tue, 24 Jan 2017 04:11:04 +0200 Subject: [PATCH 6/6] Fix spec #248 --- spec/features/registrar/domains/csv_export_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/features/registrar/domains/csv_export_spec.rb b/spec/features/registrar/domains/csv_export_spec.rb index 50fb5eb9a..ab4202118 100644 --- a/spec/features/registrar/domains/csv_export_spec.rb +++ b/spec/features/registrar/domains/csv_export_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' RSpec.feature 'CSV Export' do background do + Setting.api_ip_whitelist_enabled = false Setting.registrar_ip_whitelist_enabled = false sign_in_to_registrar_area(user: create(:api_user_with_unlimited_balance)) end