Skip to content

Commit

Permalink
added addition name variants and added punycode support for domain re…
Browse files Browse the repository at this point in the history
…servation
  • Loading branch information
OlegPhenomenon committed Aug 5, 2024
1 parent e985fc6 commit 611795b
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 20 deletions.
9 changes: 8 additions & 1 deletion app/models/reserved_domain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class ReservedDomain < ApplicationRecord
before_save :sync_dispute_password
after_destroy :remove_data

before_validation :normalize_name, on: %w[create, update]
validates :name, domain_name: true, uniqueness: true

alias_attribute :registration_code, :password
Expand Down Expand Up @@ -40,7 +41,7 @@ def new_password_for(name)
end

def name=(val)
super SimpleIDN.to_unicode(val)
super SimpleIDN.to_unicode(val).mb_chars.downcase.strip
end

def fill_empty_passwords
Expand Down Expand Up @@ -69,4 +70,10 @@ def generate_data
def remove_data
UpdateWhoisRecordJob.perform_later name, 'reserved'
end

private

def normalize_name
self.name = SimpleIDN.to_unicode(name).mb_chars.downcase.strip
end
end
7 changes: 7 additions & 0 deletions app/models/reserved_domain_status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ class ReservedDomainStatus < ApplicationRecord

enum status: { pending: 0, paid: 1, canceled: 2, failed: 3 }

before_validation :normalize_name, on: %w[create, update]
validates :name, domain_name: true

INITIATOR = 'business_registry'.freeze
OK = '200'.freeze
CREATED = '201'.freeze
Expand Down Expand Up @@ -36,6 +39,10 @@ def reserve_domain

private

def normalize_name
self.name = SimpleIDN.to_unicode(name).mb_chars.downcase.strip
end

def set_token_created_at
self.token_created_at = Time.current
end
Expand Down
34 changes: 24 additions & 10 deletions app/services/business_registry/domain_name_generator_service.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
module BusinessRegistry
class DomainNameGeneratorService
LEGAL_FORMS = %w[AS OU FIE ].freeze
LEGAL_FORMS = %w[AS OU FIE MTÜ].freeze

def self.generate(name)
base_name = remove_legal_forms(sanitize_input(name))
variants = generate_variants(base_name)
variants + generate_additional_variants(variants)
base_name = sanitize_input(name)
legal_form = extract_legal_form(base_name)
base_variants = generate_variants(base_name.sub(/\s+#{legal_form}\s*$/i, ''))
base_variants + generate_variants_with_legal_form(base_name, legal_form)
end

private
Expand All @@ -14,9 +15,9 @@ def self.sanitize_input(name)
name.gsub(/[^[:alnum:]\s\-]/, '').strip
end

def self.remove_legal_forms(name)
def self.extract_legal_form(name)
words = name.split
words.reject { |word| LEGAL_FORMS.include?(word.upcase) }.join(' ').strip
LEGAL_FORMS.find { |form| words.last.upcase == form }
end

def self.generate_variants(name)
Expand All @@ -27,9 +28,22 @@ def self.generate_variants(name)
]
end

def self.generate_additional_variants(variants)
current_year = Time.current.year
variants.map { |v| "#{v}#{current_year}" }
def self.generate_variants_with_legal_form(name, legal_form)
return [] unless legal_form

base_name = name.sub(/\s+#{legal_form}\s*$/i, '').downcase
base_variants = generate_variants(base_name)

legal_form_variants = [
legal_form.downcase,
legal_form.downcase.tr('ü', 'u'),
"-#{legal_form.downcase}",
"-#{legal_form.downcase.tr('ü', 'u')}",
"_#{legal_form.downcase}",
"_#{legal_form.downcase.tr('ü', 'u')}"
]

base_variants.product(legal_form_variants).map(&:join)
end
end
end
end
3 changes: 2 additions & 1 deletion app/validators/domain_name_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def validate_format(value)
origins = DNS::Zone.origins
# if someone tries to register an origin domain, let this validation pass
# the error will be caught in blocked domains validator
return true if origins.include?(value)
return true if origins.include?(value.split('.').last)

general_domains = /(#{origins.join('|')})/

Expand All @@ -28,6 +28,7 @@ def validate_format(value)
return false unless value.match?(regexp)

value = SimpleIDN.to_unicode(value).mb_chars.downcase.strip

end

unicode_chars = /\u00E4\u00F5\u00F6\u00FC\u0161\u017E/ # äõöüšž
Expand Down
15 changes: 15 additions & 0 deletions test/models/reserved_domain_status_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,21 @@ def setup
assert_equal @reserved_domain_status.access_token, invoice.token
end

def test_create_reserved_domain_with_punycode_name
reserved_domain = ReservedDomainStatus.create(name: 'xn--4ca7aey.test')
assert reserved_domain.valid?
end

def test_create_reserved_domain_with_unicode_name
reserved_domain = ReservedDomainStatus.create(name: 'õäöü.test')
assert reserved_domain.valid?
end

def test_cannot_to_register_invalid_domain_format
reserved_domain = ReservedDomainStatus.new(name: 'example')
assert_not reserved_domain.valid?
end

private

def stub_eis_billing_requests
Expand Down
19 changes: 19 additions & 0 deletions test/models/reserved_domain_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,23 @@ def test_aliases_registration_code_to_password
reserved_domain = ReservedDomain.new(password: 'reserved-001')
assert_equal 'reserved-001', reserved_domain.registration_code
end

def test_create_reserved_domain_with_punycode_name
reserved_domain = ReservedDomain.create(name: 'xn--4ca7aey.test')
assert reserved_domain.valid?
end

def test_create_reserved_domain_with_unicode_name
reserved_domain = ReservedDomain.create(name: 'õäöü.test')
assert reserved_domain.valid?
end

def test_cannot_create_the_same_domain_twicde_with_punycode_and_unicode
punycode_reserved_domain = ReservedDomain.new(name: 'xn--4ca7aey.test')
assert punycode_reserved_domain.valid?
punycode_reserved_domain.save && punycode_reserved_domain.reload

unicode_reserved_domain = ReservedDomain.new(name: 'õäöü.test')
assert_not unicode_reserved_domain.valid?
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,65 @@ class BusinessRegistry::DomainNameGeneratorServiceTest < ActiveSupport::TestCase
assert_includes variants, "testcompany"
assert_includes variants, "test-company"
assert_includes variants, "test_company"
assert_includes variants, "testcompany#{Time.current.year}"
end

test "should remove legal forms" do
variants = BusinessRegistry::DomainNameGeneratorService.generate("Test Company AS")
test "should generate variants with legal forms" do
variants = BusinessRegistry::DomainNameGeneratorService.generate("Test Company ")
assert_includes variants, "testcompany"
refute_includes variants, "testcompanyas"
assert_includes variants, "test-company"
assert_includes variants, "test_company"
assert_includes variants, "testcompanyoü"
assert_includes variants, "testcompanyou"
assert_includes variants, "testcompany-oü"
assert_includes variants, "testcompany-ou"
assert_includes variants, "testcompany_oü"
assert_includes variants, "testcompany_ou"
assert_includes variants, "test-companyoü"
assert_includes variants, "test-companyou"
assert_includes variants, "test-company-oü"
assert_includes variants, "test-company-ou"
assert_includes variants, "test-company_oü"
assert_includes variants, "test-company_ou"
assert_includes variants, "test_companyoü"
assert_includes variants, "test_companyou"
assert_includes variants, "test_company-oü"
assert_includes variants, "test_company-ou"
assert_includes variants, "test_company_oü"
assert_includes variants, "test_company_ou"
end

test "should generate variants with current year" do
variants = BusinessRegistry::DomainNameGeneratorService.generate("Test")
assert_includes variants, "test#{Time.current.year}"
test "should handle different legal forms" do
legal_forms = %w[AS OU FIE MTÜ]
legal_forms.each do |form|
variants = BusinessRegistry::DomainNameGeneratorService.generate("Test Company #{form}")
assert_includes variants, "testcompany#{form.downcase}"
assert_includes variants, "testcompany-#{form.downcase}"
assert_includes variants, "testcompany_#{form.downcase}"
assert_includes variants, "test-company#{form.downcase}"
assert_includes variants, "test-company-#{form.downcase}"
assert_includes variants, "test-company_#{form.downcase}"
assert_includes variants, "test_company#{form.downcase}"
assert_includes variants, "test_company-#{form.downcase}"
assert_includes variants, "test_company_#{form.downcase}"

if form.include?('Ü')
alternative_form = form.tr('Ü', 'U').downcase
assert_includes variants, "testcompany#{alternative_form}"
assert_includes variants, "testcompany-#{alternative_form}"
assert_includes variants, "testcompany_#{alternative_form}"
assert_includes variants, "test-company#{alternative_form}"
assert_includes variants, "test-company-#{alternative_form}"
assert_includes variants, "test-company_#{alternative_form}"
assert_includes variants, "test_company#{alternative_form}"
assert_includes variants, "test_company-#{alternative_form}"
assert_includes variants, "test_company_#{alternative_form}"
end
end
end

test "should handle special characters" do
variants = BusinessRegistry::DomainNameGeneratorService.generate("Test & Company!")
assert_includes variants, "testcompany"
refute_includes variants, "test&company"
end
end
end

0 comments on commit 611795b

Please sign in to comment.