Skip to content

Synchronise subdomain domain CRUD actions in the database with a Heroku instance via Heroku's Platform API

Rui edited this page Nov 11, 2020 · 2 revisions

Taken from @ben-gy ticket

I've spent a lot of time consolidating information creating a pattern that uses a series of callbacks on a Tenant model that synchronises subdomain/domain CRUD actions in the database with a Heroku instance via Heroku's Platform API. I think this could be useful for a lot of people using this gem. For now, here's a snapshot of what I've done...

class Organisation < ApplicationRecord

  # version control
  has_paper_trail

  # validations
  validates :name, length: { in: 3..100 }, presence: true
  validates :subdomain, length: { in: 3..20 }, presence: true, uniqueness: true

  validates { subdomain_available_on_heroku } unless Rails.env.development?

  after_create { create_tenant }
  after_destroy { remove_tenant }
  after_save  { rename_tenant }

  private

    def subdomain_available_on_heroku
      # get list of domains from Heroku
      domain_list = heroku.domain.list(app_id)

      # check if domains include the subdomain and abort if so
      if domain_list.include?(self.subdomain)
        errors.add(:subdomain, 'something has gone wrong - the subdomain is available in the database but not available on Heroku')
        throw(:abort)
      end
    end

    def create_tenant
      Apartment::Tenant.create(self.subdomain)

      # create the subdomain on heroku
      unless Rails.env.development?
        heroku.domain.create(ENV['HEROKU_APP_ID'], "#{self.subdomain}.#{ENV['DOMAIN_NAME']}")
      end
    end

    def remove_tenant
      Apartment::Tenant.drop(self.subdomain)

      # cleanup the subdomain on heroku
      unless Rails.env.development?
        heroku.domain.delete(ENV['HEROKU_APP_ID'], "#{self.subdomain}.#{ENV['DOMAIN_NAME']}")
      end
    end

    def rename_tenant
      if self.paper_trail.previous_version.present? && self.paper_trail.previous_version.subdomain != self.subdomain
        ActiveRecord::Base.connection.execute("ALTER SCHEMA \"#{self.paper_trail.previous_version.subdomain}\" RENAME TO \"#{self.subdomain}\";")

        # cleanup the subdomain on heroku
        unless Rails.env.development?
          heroku.domain.delete(ENV['HEROKU_APP_ID'], "#{self.paper_trail.previous_version.subdomain}.#{ENV['DOMAIN_NAME']}")
          heroku.domain.create(ENV['HEROKU_APP_ID'], "#{self.subdomain}.#{ENV['DOMAIN_NAME']}")
        end
      end
    end
end