diff --git a/.codeclimate.yml b/.codeclimate.yml index ffcd2856cf..3869f2a950 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -31,7 +31,7 @@ plugins: enabled: false rubocop: enabled: true - channel: rubocop-1-18-3 + channel: rubocop-1-45 checks: Rubocop/Style/ClassAndModuleChildren: enabled: false diff --git a/app/controllers/repp/v1/stats_controller.rb b/app/controllers/repp/v1/stats_controller.rb index 052cdcbc45..b8c3b053fc 100644 --- a/app/controllers/repp/v1/stats_controller.rb +++ b/app/controllers/repp/v1/stats_controller.rb @@ -9,7 +9,7 @@ class StatsController < BaseController # rubocop:disable Metrics/ClassLength param :end_date, String, required: true, desc: 'Period end date' end def market_share_distribution - domains_by_rar = domains_by_registrar(@date_to, @date_from) + domains_by_rar = domains_by_registrar(@date_to) result = serialize_distribution_result(domains_by_rar) render_success(data: result) end @@ -22,8 +22,8 @@ def market_share_distribution param :compare_to_end_date, String, required: true, desc: 'Comparison date' end def market_share_growth_rate - domains_by_rar = domains_by_registrar(@date_to, @date_from) - prev_domains_by_rar = domains_by_registrar(@date_compare_to, @date_compare_from) + domains_by_rar = domains_by_registrar(@date_to) + prev_domains_by_rar = domains_by_registrar(@date_compare_to) set_zero_values!(domains_by_rar, prev_domains_by_rar) @@ -36,6 +36,7 @@ def market_share_growth_rate data: { name: search_params[:end_date], domains: serialize_growth_rate_result(domains_by_rar), market_share: serialize_growth_rate_result(market_share_by_rar) } } + render_success(data: result) end # rubocop:enable Metrics/MethodLength @@ -43,15 +44,13 @@ def market_share_growth_rate private def search_params - params.permit(:q, q: %i[start_date end_date compare_to_end_date compare_to_start_date]) + params.permit(:q, q: %i[end_date compare_to_end_date]) .fetch(:q, {}) || {} end def set_date_params @date_to = to_date(search_params[:end_date]).end_of_month - @date_from = to_date(search_params[:start_date] || '01.05') @date_compare_to = to_date(search_params[:compare_to_end_date]).end_of_month - @date_compare_from = to_date(search_params[:compare_to_start_date] || '01.05') end def to_date(date_param) @@ -78,35 +77,108 @@ def calculate_market_share(domains_by_rar) end end - def domains_by_registrar(date_to, date_from) - log_domains_del = log_domains(event: 'destroy', date_to: date_to, date_from: date_from) - log_domains_trans = log_domains(event: 'update', date_to: date_to, date_from: date_from) - logged_domains = log_domains_trans.map { |ld| ld.object['name'] } + - log_domains_del.map { |ld| ld.object['name'] } - domains_grouped = ::Domain.where('created_at <= ? AND created_at >= ?', date_to, date_from) - .where.not(name: logged_domains.uniq) - .group(:registrar_id).count.stringify_keys - summarize([group(log_domains_del), group(log_domains_trans), domains_grouped]) - end - - def summarize(arr) - arr.inject { |memo, el| memo.merge(el) { |_, old_v, new_v| old_v + new_v } } - end - - def log_domains(event:, date_to:, date_from:) - domains = ::Version::DomainVersion.where(event: event) - domains.where!("object_changes ->> 'registrar_id' IS NOT NULL") if event == 'update' - domains.where('created_at > ?', date_to) - .where("object ->> 'created_at' <= ?", date_to) - .where("object ->> 'created_at' >= ?", date_from) - .select("DISTINCT ON (object ->> 'name') object, created_at") - .order(Arel.sql("object ->> 'name', created_at desc")) - end - - def group(domains) - domains.group_by { |ld| ld.object['registrar_id'].to_s } - .transform_values(&:count) + # rubocop:disable Metrics/MethodLength + def domains_by_registrar(date_to) + sql = <<-SQL + SELECT + registrar_id, + SUM(domain_count) AS total_domain_count + FROM ( + -- Query for current domains for each registrar + SELECT + registrar_id::text AS registrar_id, + COUNT(*) AS domain_count + FROM + domains + GROUP BY + registrar_id + + UNION ALL + + -- Query for 'create' events and count domains created by registrar + SELECT + (object_changes->'registrar_id'->>1)::text AS registrar_id, + COUNT(*) * -1 AS domain_count + FROM + log_domains + WHERE + event = 'create' + AND created_at > :date_to + GROUP BY + registrar_id + + UNION ALL + + -- Query for 'update' events and count domains transferred to a new registrar + SELECT + (object_changes->'registrar_id'->>1)::text AS registrar_id, + COUNT(*) * -1 AS domain_count + FROM + log_domains + WHERE + event = 'update' + AND object_changes->'registrar_id' IS NOT NULL + AND created_at > :date_to + GROUP BY + registrar_id + + UNION ALL + + -- Query for 'update' events and count domains transferred from an old registrar + SELECT + (object_changes->'registrar_id'->>0)::text AS registrar_id, + COUNT(*) AS domain_count + FROM + log_domains + WHERE + event = 'update' + AND object_changes->'registrar_id' IS NOT NULL + AND created_at > :date_to + GROUP BY + registrar_id + + UNION ALL + + -- Query for 'destroy' events and count the number of domains destroyed associated with each registrar + SELECT + (object_changes->'registrar_id'->>0)::text AS registrar_id, + COUNT(*) AS domain_count + FROM + log_domains + WHERE + event = 'destroy' + AND object_changes->'registrar_id' IS NOT NULL + AND created_at > :date_to + GROUP BY + registrar_id + + UNION ALL + + -- Query for 'destroy' events and count the number of domains destroyed associated with each registrar + SELECT + (object->'registrar_id')::text AS registrar_id, + COUNT(*) AS domain_count + FROM + log_domains + WHERE + event = 'destroy' + AND object_changes IS NULL + AND created_at > :date_to + GROUP BY + registrar_id + + ) AS combined + GROUP BY + registrar_id; + SQL + + ActiveRecord::Base.connection.execute( + ActiveRecord::Base.send(:sanitize_sql_array, [sql, { date_to: date_to }]) + ).each_with_object({}) do |row, hash| + hash[row['registrar_id']] = row['total_domain_count'].to_i + end end + # rubocop:enable Metrics/MethodLength def registrar_names @registrar_names ||= ::Registrar.where(test_registrar: false) @@ -120,7 +192,10 @@ def serialize_distribution_result(result) name = registrar_names[key] hash = { name: name, y: value } - hash.merge!({ sliced: true, selected: true }) if current_user.registrar.name == name + if current_user.registrar.name == name + hash[:sliced] = true + hash[:selected] = true + end hash end.compact end diff --git a/test/fixtures/domains.yml b/test/fixtures/domains.yml index 01c43d644d..770559bdf0 100644 --- a/test/fixtures/domains.yml +++ b/test/fixtures/domains.yml @@ -12,7 +12,7 @@ shop: period: 1 period_unit: m uuid: 1b3ee442-e8fe-4922-9492-8fcb9dccc69c - created_at: <%= 2.days.ago.to_s :db %> + created_at: <%= 2.months.ago.to_s :db %> airport: name: airport.test @@ -51,7 +51,7 @@ metro: period: 1 period_unit: m uuid: ef97cb80-333b-4893-b9df-163f2b452798 - created_at: <%= 2.days.ago.to_s :db %> + created_at: <%= 3.months.ago.to_s :db %> hospital: name: hospital.test diff --git a/test/fixtures/files/domain_versions.csv b/test/fixtures/files/domain_versions.csv index 8fa7fa9fe8..52492e1539 100644 --- a/test/fixtures/files/domain_versions.csv +++ b/test/fixtures/files/domain_versions.csv @@ -1,3 +1,12 @@ Name,Registrant,Registrar,Action,Created at +,John,,destroy,2023-12-04 22:00:00 +hospital.test,John,Good Names,create,2023-12-04 22:00:00 +library.test,Acme Ltd,Best Names,create,2023-12-04 22:00:00 +shop.test,John,Best Names,update,2023-12-04 22:00:00 +invalid.test,any,Best Names,create,2023-12-04 22:00:00 +airport.test,John,Best Names,create,2023-12-04 22:00:00 +shop.test,John,Good Names,create,2023-10-04 21:00:00 +cinema.test,John,Good Names,create,2023-09-04 21:00:00 +metro.test,Jack,Good Names,create,2023-09-04 21:00:00 ,test_code,Best Names,update,2018-04-23 15:50:48 ,John,,update,2010-07-04 21:00:00 diff --git a/test/fixtures/log_domains.yml b/test/fixtures/log_domains.yml index f328f902a6..bbd161c64e 100644 --- a/test/fixtures/log_domains.yml +++ b/test/fixtures/log_domains.yml @@ -5,4 +5,96 @@ one: object: registrant_id: <%= ActiveRecord::FixtureSet.identify(:john) %> updated_at: <%= Time.zone.parse('2010-07-05') %> - created_at: <%= Time.zone.parse('2010-07-05') %> \ No newline at end of file + created_at: <%= Time.zone.parse('2010-07-05') %> + +create_one: + item_id: <%= ActiveRecord::FixtureSet.identify(:shop) %> + item_type: Domain + event: create + object_changes: + name: [null, 'shop.test'] + registrar_id: [null, <%= ActiveRecord::FixtureSet.identify(:goodnames) %>] + registrant_id: [null, <%= ActiveRecord::FixtureSet.identify(:john) %>] + created_at: <%= Time.zone.parse('2023-10-05') %> + +create_two: + item_id: <%= ActiveRecord::FixtureSet.identify(:airport) %> + item_type: Domain + event: create + object_changes: + name: [null, 'airport.test'] + registrar_id: [null, <%= ActiveRecord::FixtureSet.identify(:bestnames) %>] + registrant_id: [null, <%= ActiveRecord::FixtureSet.identify(:john) %>] + created_at: <%= Time.zone.parse('2023-12-05') %> + +create_three: + item_id: <%= ActiveRecord::FixtureSet.identify(:hospital) %> + item_type: Domain + event: create + object_changes: + name: [null, 'hospital.test'] + registrar_id: [null, <%= ActiveRecord::FixtureSet.identify(:goodnames) %>] + registrant_id: [null, <%= ActiveRecord::FixtureSet.identify(:john) %>] + created_at: <%= Time.zone.parse('2023-12-05') %> + +create_four: + item_id: <%= ActiveRecord::FixtureSet.identify(:invalid) %> + item_type: Domain + event: create + object_changes: + name: [null, 'invalid.test'] + registrar_id: [null, <%= ActiveRecord::FixtureSet.identify(:bestnames) %>] + registrant_id: [null, <%= ActiveRecord::FixtureSet.identify(:invalid) %>] + created_at: <%= Time.zone.parse('2023-12-05') %> + +transfer_one: + item_id: <%= ActiveRecord::FixtureSet.identify(:shop) %> + item_type: Domain + event: update + object: + name: 'shop.test' + registrant_id: <%= ActiveRecord::FixtureSet.identify(:john) %> + object_changes: + registrar_id: [<%= ActiveRecord::FixtureSet.identify(:goodnames) %>, <%= ActiveRecord::FixtureSet.identify(:bestnames) %>] + created_at: <%= Time.zone.parse('2023-12-05') %> + +create_five: + item_id: 1111111 + item_type: Domain + event: create + object_changes: + name: [null, 'cinema.test'] + registrar_id: [null, <%= ActiveRecord::FixtureSet.identify(:goodnames) %>] + registrant_id: [null, <%= ActiveRecord::FixtureSet.identify(:john) %>] + created_at: <%= Time.zone.parse('2023-09-05') %> + +destroy_one: + item_id: 1111111 + item_type: Domain + event: destroy + object: + registrant_id: <%= ActiveRecord::FixtureSet.identify(:john) %> + object_changes: + name: ['cinema.test', null] + registrar_id: [<%= ActiveRecord::FixtureSet.identify(:goodnames) %>, null] + created_at: <%= Time.zone.parse('2023-12-05') %> + +create_six: + item_id: <%= ActiveRecord::FixtureSet.identify(:library) %> + item_type: Domain + event: create + object_changes: + name: [null, 'library.test'] + registrar_id: [null, <%= ActiveRecord::FixtureSet.identify(:bestnames) %>] + registrant_id: [null, <%= ActiveRecord::FixtureSet.identify(:acme_ltd) %>] + created_at: <%= Time.zone.parse('2023-12-05') %> + +create_seven: + item_id: <%= ActiveRecord::FixtureSet.identify(:metro) %> + item_type: Domain + event: create + object_changes: + name: [null, 'metro.test'] + registrar_id: [null, <%= ActiveRecord::FixtureSet.identify(:goodnames) %>] + registrant_id: [null, <%= ActiveRecord::FixtureSet.identify(:jack) %>] + created_at: <%= Time.zone.parse('2023-09-05') %> \ No newline at end of file diff --git a/test/integration/repp/v1/stats/market_share_test.rb b/test/integration/repp/v1/stats/market_share_test.rb index d3acba89b9..2f129d8439 100644 --- a/test/integration/repp/v1/stats/market_share_test.rb +++ b/test/integration/repp/v1/stats/market_share_test.rb @@ -19,8 +19,8 @@ def test_shows_market_share_distribution_data assert_equal 1000, json[:code] assert_equal 'Command completed successfully', json[:message] - assert json[:data].is_a? Array - assert json[:data][0].is_a? Hash + assert_equal json[:data], [{ name: 'Good Names', y: 2 }, + { name: @user.registrar.name, y: 4, sliced: true, selected: true }] end def test_shows_market_share_growth_rate_data @@ -34,10 +34,11 @@ def test_shows_market_share_growth_rate_data assert_equal 1000, json[:code] assert_equal 'Command completed successfully', json[:message] - data = json[:data] - assert data[:data].is_a? Hash - assert data[:prev_data].is_a? Hash - assert_equal data[:data][:name], @today - assert data[:data][:domains].is_a? Array + assert_equal json[:data], prev_data: { name: prev_date, + domains: [['Good Names', 3], ['Best Names', 0]], + market_share: [['Good Names', 100.0], ['Best Names', 0.0]] }, + data: { name: @today, + domains: [['Good Names', 2], ['Best Names', 4]], + market_share: [['Good Names', 33.3], ['Best Names', 66.7]] } end -end \ No newline at end of file +end diff --git a/test/learning/paper_trail_test.rb b/test/learning/paper_trail_test.rb index 9e116bf639..bd6c7993e0 100644 --- a/test/learning/paper_trail_test.rb +++ b/test/learning/paper_trail_test.rb @@ -24,13 +24,14 @@ def test_returns_version_list def test_returns_version_count_on_domains @domain = domains(:airport) - @domain.save - - assert_equal 1, @domain.versions.count + assert_difference -> { @domain.versions.count }, 1 do + @domain.save + end @domain.name = 'domain.test' - @domain.save! - assert_equal 2, @domain.versions.count + assert_difference -> { @domain.versions.count }, 1 do + @domain.save! + end end def test_returns_version_count_on_users