From 72704d7761c77d060f9c0b140b3d70bc454d8787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 15 Dec 2022 17:43:57 +0100 Subject: [PATCH 1/4] Use separate actions to enable/disable budget phases This is consistent with the way we use separate actions to hide and restore records, which is similar to enabling and disabling a record. We might do something similar with the `toggle_selection` actions in the future. For now, we're only doing it with budget phases because we're going to add a similar switch control to hide and restore tenants. We're also making these actions idempotent, so sending many requests to the same action will get the same result, which wasn't the case with the `toggle` action. Although it's a low probability case, the `toggle` action could result in disabling a phase when trying to enable it if someone else has enabled it between the time the page loaded and the time the admin clicked on the "enable" button. --- .../toggle_enabled_component.html.erb | 2 +- .../budget_phases/toggle_enabled_component.rb | 14 ++++++++++++-- .../concerns/admin/budget_phases_actions.rb | 17 +++++++++++++---- .../admin/budget_phases/toggle_enabled.js.erb | 1 - .../budgets_wizard/phases/toggle_enabled.js.erb | 5 +++-- config/routes/admin.rb | 10 ++++++++-- 6 files changed, 37 insertions(+), 12 deletions(-) delete mode 100644 app/views/admin/budget_phases/toggle_enabled.js.erb diff --git a/app/components/admin/budget_phases/toggle_enabled_component.html.erb b/app/components/admin/budget_phases/toggle_enabled_component.html.erb index d230e97e5..d84202d46 100644 --- a/app/components/admin/budget_phases/toggle_enabled_component.html.erb +++ b/app/components/admin/budget_phases/toggle_enabled_component.html.erb @@ -1 +1 @@ -<%= render Admin::ActionComponent.new(:toggle_enabled, phase, options) %> +<%= render Admin::ActionComponent.new(action, phase, options) %> diff --git a/app/components/admin/budget_phases/toggle_enabled_component.rb b/app/components/admin/budget_phases/toggle_enabled_component.rb index 0236cdae5..5c270050c 100644 --- a/app/components/admin/budget_phases/toggle_enabled_component.rb +++ b/app/components/admin/budget_phases/toggle_enabled_component.rb @@ -1,5 +1,6 @@ class Admin::BudgetPhases::ToggleEnabledComponent < ApplicationComponent attr_reader :phase + delegate :enabled?, to: :phase def initialize(phase) @phase = phase @@ -13,12 +14,21 @@ class Admin::BudgetPhases::ToggleEnabledComponent < ApplicationComponent method: :patch, remote: true, "aria-label": t("admin.budgets.edit.enable_phase", phase: phase.name), - "aria-pressed": phase.enabled? + "aria-pressed": enabled?, + form_class: "toggle-switch" } end + def action + if enabled? + :disable + else + :enable + end + end + def text - if phase.enabled? + if enabled? t("shared.yes") else t("shared.no") diff --git a/app/controllers/concerns/admin/budget_phases_actions.rb b/app/controllers/concerns/admin/budget_phases_actions.rb index 8c267c364..ff38f196a 100644 --- a/app/controllers/concerns/admin/budget_phases_actions.rb +++ b/app/controllers/concerns/admin/budget_phases_actions.rb @@ -6,7 +6,7 @@ module Admin::BudgetPhasesActions include ImageAttributes before_action :load_budget - before_action :load_phase, only: [:edit, :update, :toggle_enabled] + before_action :load_phase, only: [:edit, :update, :enable, :disable] end def edit @@ -20,12 +20,21 @@ module Admin::BudgetPhasesActions end end - def toggle_enabled - @phase.update!(enabled: !@phase.enabled) + def enable + @phase.update!(enabled: true) respond_to do |format| format.html { redirect_to phases_index, notice: t("flash.actions.save_changes.notice") } - format.js + format.js { render template: "admin/budgets_wizard/phases/toggle_enabled" } + end + end + + def disable + @phase.update!(enabled: false) + + respond_to do |format| + format.html { redirect_to phases_index, notice: t("flash.actions.save_changes.notice") } + format.js { render template: "admin/budgets_wizard/phases/toggle_enabled" } end end diff --git a/app/views/admin/budget_phases/toggle_enabled.js.erb b/app/views/admin/budget_phases/toggle_enabled.js.erb deleted file mode 100644 index b90a979bd..000000000 --- a/app/views/admin/budget_phases/toggle_enabled.js.erb +++ /dev/null @@ -1 +0,0 @@ -<%= render template: "admin/budgets_wizard/phases/toggle_enabled" %> diff --git a/app/views/admin/budgets_wizard/phases/toggle_enabled.js.erb b/app/views/admin/budgets_wizard/phases/toggle_enabled.js.erb index d4424bf4c..d0688ab25 100644 --- a/app/views/admin/budgets_wizard/phases/toggle_enabled.js.erb +++ b/app/views/admin/budgets_wizard/phases/toggle_enabled.js.erb @@ -1,4 +1,5 @@ var replacement = $("<%= j render Admin::BudgetPhases::ToggleEnabledComponent.new(@phase) %>"); -var form = $("#" + replacement.find("[type='submit']").attr("id")).closest("form"); +var form = $("#<%= dom_id(@phase) %> .toggle-switch"); -form.html(replacement.html()).find("[type='submit']").focus(); +form.replaceWith(replacement); +replacement.find("[type='submit']").focus(); diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 752942b82..af7d96cec 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -70,7 +70,10 @@ namespace :admin do end resources :budget_phases, only: [:edit, :update] do - member { patch :toggle_enabled } + member do + patch :enable + patch :disable + end end end @@ -81,7 +84,10 @@ namespace :admin do end resources :phases, as: "budget_phases", only: [:index, :edit, :update] do - member { patch :toggle_enabled } + member do + patch :enable + patch :disable + end end end end From 0d18e25e9922650901d62ecf15046d8a204c20ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 16 Dec 2022 15:35:08 +0100 Subject: [PATCH 2/4] Extract component to render a switch We're going to use it in other places. --- .../admin/budget_phases/phases.scss | 5 --- .../stylesheets/admin/toggle_switch.scss | 6 ++++ .../toggle_enabled_component.html.erb | 2 +- .../budget_phases/toggle_enabled_component.rb | 17 +--------- .../admin/toggle_switch_component.html.erb | 1 + .../admin/toggle_switch_component.rb | 31 +++++++++++++++++++ 6 files changed, 40 insertions(+), 22 deletions(-) create mode 100644 app/assets/stylesheets/admin/toggle_switch.scss create mode 100644 app/components/admin/toggle_switch_component.html.erb create mode 100644 app/components/admin/toggle_switch_component.rb diff --git a/app/assets/stylesheets/admin/budget_phases/phases.scss b/app/assets/stylesheets/admin/budget_phases/phases.scss index d627cf055..7d84f5231 100644 --- a/app/assets/stylesheets/admin/budget_phases/phases.scss +++ b/app/assets/stylesheets/admin/budget_phases/phases.scss @@ -3,9 +3,4 @@ caption { @include element-invisible; } - - [aria-pressed] { - @include switch; - margin-bottom: 0; - } } diff --git a/app/assets/stylesheets/admin/toggle_switch.scss b/app/assets/stylesheets/admin/toggle_switch.scss new file mode 100644 index 000000000..501e85eaa --- /dev/null +++ b/app/assets/stylesheets/admin/toggle_switch.scss @@ -0,0 +1,6 @@ +.admin .toggle-switch { + [aria-pressed] { + @include switch; + margin-bottom: 0; + } +} diff --git a/app/components/admin/budget_phases/toggle_enabled_component.html.erb b/app/components/admin/budget_phases/toggle_enabled_component.html.erb index d84202d46..832049577 100644 --- a/app/components/admin/budget_phases/toggle_enabled_component.html.erb +++ b/app/components/admin/budget_phases/toggle_enabled_component.html.erb @@ -1 +1 @@ -<%= render Admin::ActionComponent.new(action, phase, options) %> +<%= render Admin::ToggleSwitchComponent.new(action, phase, pressed: enabled?, **options) %> diff --git a/app/components/admin/budget_phases/toggle_enabled_component.rb b/app/components/admin/budget_phases/toggle_enabled_component.rb index 5c270050c..daf2693b1 100644 --- a/app/components/admin/budget_phases/toggle_enabled_component.rb +++ b/app/components/admin/budget_phases/toggle_enabled_component.rb @@ -9,14 +9,7 @@ class Admin::BudgetPhases::ToggleEnabledComponent < ApplicationComponent private def options - { - text: text, - method: :patch, - remote: true, - "aria-label": t("admin.budgets.edit.enable_phase", phase: phase.name), - "aria-pressed": enabled?, - form_class: "toggle-switch" - } + { "aria-label": t("admin.budgets.edit.enable_phase", phase: phase.name) } end def action @@ -26,12 +19,4 @@ class Admin::BudgetPhases::ToggleEnabledComponent < ApplicationComponent :enable end end - - def text - if enabled? - t("shared.yes") - else - t("shared.no") - end - end end diff --git a/app/components/admin/toggle_switch_component.html.erb b/app/components/admin/toggle_switch_component.html.erb new file mode 100644 index 000000000..35b566c48 --- /dev/null +++ b/app/components/admin/toggle_switch_component.html.erb @@ -0,0 +1 @@ +<%= render Admin::ActionComponent.new(action, record, **default_options.merge(options)) %> diff --git a/app/components/admin/toggle_switch_component.rb b/app/components/admin/toggle_switch_component.rb new file mode 100644 index 000000000..ceee74d5b --- /dev/null +++ b/app/components/admin/toggle_switch_component.rb @@ -0,0 +1,31 @@ +class Admin::ToggleSwitchComponent < ApplicationComponent + attr_reader :action, :record, :pressed, :options + alias_method :pressed?, :pressed + + def initialize(action, record, pressed:, **options) + @action = action + @record = record + @pressed = pressed + @options = options + end + + private + + def text + if pressed? + t("shared.yes") + else + t("shared.no") + end + end + + def default_options + { + text: text, + method: :patch, + remote: true, + "aria-pressed": pressed?, + form_class: "toggle-switch" + } + end +end From 0dac6ead77004b007b4058714ca109346016950c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 19 Dec 2022 15:12:08 +0100 Subject: [PATCH 3/4] Raise an exception when accessing a domain as subdomain We were returning `nil` in this case, meaning we would be accessing the main tenant in this situation instead of returning "Not Found". --- app/models/tenant.rb | 6 +++++- spec/models/tenant_spec.rb | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/models/tenant.rb b/app/models/tenant.rb index 54a254c39..ffbba3442 100644 --- a/app/models/tenant.rb +++ b/app/models/tenant.rb @@ -30,7 +30,11 @@ class Tenant < ApplicationRecord host_domain = allowed_domains.find { |domain| host == domain || host.ends_with?(".#{domain}") } schema = host_without_www.sub(/\.?#{host_domain}\Z/, "").presence - schema unless find_by_domain(schema) + if find_by_domain(schema) + raise Apartment::TenantNotFound + else + schema + end end end diff --git a/spec/models/tenant_spec.rb b/spec/models/tenant_spec.rb index e6d4bae67..b15dc7560 100644 --- a/spec/models/tenant_spec.rb +++ b/spec/models/tenant_spec.rb @@ -86,10 +86,10 @@ describe Tenant do expect(Tenant.resolve_host("saturn.consul.dev")).to eq "saturn" end - it "returns nil when a domain is accessed as a subdomain" do + it "raises an exception when a domain is accessed as a subdomain" do insert(:tenant, :domain, schema: "saturn.dev") - expect(Tenant.resolve_host("saturn.dev.consul.dev")).to be nil + expect { Tenant.resolve_host("saturn.dev.consul.dev") }.to raise_exception(Apartment::TenantNotFound) end it "returns nested subdomains when there's a subdomain-type tenant with nested subdomains" do From 25435b0297acf546707ae5c4c1edbb764790f667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Mon, 19 Dec 2022 16:32:31 +0100 Subject: [PATCH 4/4] Make it possible to disable tenants Note we could use `acts_as_paranoid` with the `without_default_scope` option, but we aren't doing so because it isn't possible to consider deleted records in uniqueness validations with the paranoia gem [1]. I've added tests for these cases so we don't accidentally add `acts_as_paranoid` in the future. Also note we're extracting a `RowComponent` because, when enabling/disabling a tenant, we're also enabling/disabling the link pointing to its URL, and so we need to update the URL column after the AJAX call. [1] See issues 285 and 319 in https://github.com/rubysherpas/paranoia/ --- .rubocop.yml | 1 + .../admin/tenants/index_component.html.erb | 10 +---- .../admin/tenants/row_component.html.erb | 9 ++++ app/components/admin/tenants/row_component.rb | 7 ++++ .../tenants/toggle_hidden_component.html.erb | 1 + .../admin/tenants/toggle_hidden_component.rb | 28 +++++++++++++ app/controllers/admin/tenants_controller.rb | 18 ++++++++ app/models/abilities/administrator.rb | 2 +- app/models/tenant.rb | 24 +++++++++++ app/views/admin/tenants/toggle_enabled.js.erb | 4 ++ config/locales/en/admin.yml | 6 +++ config/locales/es/admin.yml | 6 +++ config/routes/admin.rb | 7 +++- ...20221203140136_add_hidden_at_to_tenants.rb | 5 +++ db/schema.rb | 3 +- spec/models/abilities/administrator_spec.rb | 4 ++ spec/models/tenant_spec.rb | 41 +++++++++++++++++++ spec/system/admin/tenants_spec.rb | 39 ++++++++++++++++++ 18 files changed, 204 insertions(+), 11 deletions(-) create mode 100644 app/components/admin/tenants/row_component.html.erb create mode 100644 app/components/admin/tenants/row_component.rb create mode 100644 app/components/admin/tenants/toggle_hidden_component.html.erb create mode 100644 app/components/admin/tenants/toggle_hidden_component.rb create mode 100644 app/views/admin/tenants/toggle_enabled.js.erb create mode 100644 db/migrate/20221203140136_add_hidden_at_to_tenants.rb diff --git a/.rubocop.yml b/.rubocop.yml index 9e252d614..f47dd6fb6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -335,6 +335,7 @@ Rails/SkipsModelValidations: ForbiddenMethods: - update_attribute Exclude: + - app/models/tenant.rb - lib/acts_as_paranoid_aliases.rb Rails/TimeZone: diff --git a/app/components/admin/tenants/index_component.html.erb b/app/components/admin/tenants/index_component.html.erb index ad5f43ea9..63bb3504e 100644 --- a/app/components/admin/tenants/index_component.html.erb +++ b/app/components/admin/tenants/index_component.html.erb @@ -8,20 +8,14 @@ <%= attribute_name(:name) %> <%= attribute_name(:schema) %> <%= attribute_name(:url) %> + <%= t("admin.tenants.index.enabled") %> <%= t("admin.shared.actions") %> <% @tenants.each do |tenant| %> - - <%= tenant.name %> - <%= tenant.schema %> - <%= link_to tenant.host, root_url(host: tenant.host) %> - - <%= render Admin::TableActionsComponent.new(tenant, actions: [:edit]) %> - - + <%= render Admin::Tenants::RowComponent.new(tenant) %> <% end %> diff --git a/app/components/admin/tenants/row_component.html.erb b/app/components/admin/tenants/row_component.html.erb new file mode 100644 index 000000000..365866676 --- /dev/null +++ b/app/components/admin/tenants/row_component.html.erb @@ -0,0 +1,9 @@ + + <%= tenant.name %> + <%= tenant.schema %> + <%= link_to_unless tenant.hidden?, tenant.host, root_url(host: tenant.host) %> + <%= render Admin::Tenants::ToggleHiddenComponent.new(tenant) %> + + <%= render Admin::TableActionsComponent.new(tenant, actions: [:edit]) %> + + diff --git a/app/components/admin/tenants/row_component.rb b/app/components/admin/tenants/row_component.rb new file mode 100644 index 000000000..648b0db7a --- /dev/null +++ b/app/components/admin/tenants/row_component.rb @@ -0,0 +1,7 @@ +class Admin::Tenants::RowComponent < ApplicationComponent + attr_reader :tenant + + def initialize(tenant) + @tenant = tenant + end +end diff --git a/app/components/admin/tenants/toggle_hidden_component.html.erb b/app/components/admin/tenants/toggle_hidden_component.html.erb new file mode 100644 index 000000000..64a3a2e93 --- /dev/null +++ b/app/components/admin/tenants/toggle_hidden_component.html.erb @@ -0,0 +1 @@ +<%= render Admin::ToggleSwitchComponent.new(action, tenant, pressed: enabled?, **options) %> diff --git a/app/components/admin/tenants/toggle_hidden_component.rb b/app/components/admin/tenants/toggle_hidden_component.rb new file mode 100644 index 000000000..12488ce21 --- /dev/null +++ b/app/components/admin/tenants/toggle_hidden_component.rb @@ -0,0 +1,28 @@ +class Admin::Tenants::ToggleHiddenComponent < ApplicationComponent + attr_reader :tenant + + def initialize(tenant) + @tenant = tenant + end + + private + + def action + if enabled? + :hide + else + :restore + end + end + + def options + { + method: :put, + "aria-label": t("admin.tenants.index.enable", tenant: tenant.name) + } + end + + def enabled? + !tenant.hidden? + end +end diff --git a/app/controllers/admin/tenants_controller.rb b/app/controllers/admin/tenants_controller.rb index a43e88166..e7d6e949a 100644 --- a/app/controllers/admin/tenants_controller.rb +++ b/app/controllers/admin/tenants_controller.rb @@ -27,6 +27,24 @@ class Admin::TenantsController < Admin::BaseController end end + def hide + @tenant.hide + + respond_to do |format| + format.html { redirect_to admin_tenants_path, notice: t("admin.tenants.hide.notice") } + format.js { render template: "admin/tenants/toggle_enabled" } + end + end + + def restore + @tenant.restore + + respond_to do |format| + format.html { redirect_to admin_tenants_path, notice: t("admin.tenants.restore.notice") } + format.js { render template: "admin/tenants/toggle_enabled" } + end + end + private def tenant_params diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index 9e3556e80..f874af58b 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -136,7 +136,7 @@ module Abilities can [:create, :read], LocalCensusRecords::Import if Rails.application.config.multitenancy && Tenant.default? - can [:create, :read, :update], Tenant + can [:create, :read, :update, :hide, :restore], Tenant end end end diff --git a/app/models/tenant.rb b/app/models/tenant.rb index ffbba3442..654cbecf0 100644 --- a/app/models/tenant.rb +++ b/app/models/tenant.rb @@ -12,6 +12,8 @@ class Tenant < ApplicationRecord after_update :rename_schema after_destroy :destroy_schema + scope :only_hidden, -> { where.not(hidden_at: nil) } + def self.find_by_domain(host) domain.find_by(schema: host) end @@ -20,6 +22,16 @@ class Tenant < ApplicationRecord return nil unless Rails.application.config.multitenancy.present? return nil if host.blank? || host.match?(Resolv::AddressRegex) + schema = schema_for(host) + + if schema && only_hidden.find_by(schema: schema) + raise Apartment::TenantNotFound + else + schema + end + end + + def self.schema_for(host) host_without_www = host.delete_prefix("www.") if find_by_domain(host) @@ -137,6 +149,18 @@ class Tenant < ApplicationRecord self.class.host_for(schema) end + def hide + update_attribute(:hidden_at, Time.current) + end + + def restore + update_attribute(:hidden_at, nil) + end + + def hidden? + hidden_at.present? + end + private def create_schema diff --git a/app/views/admin/tenants/toggle_enabled.js.erb b/app/views/admin/tenants/toggle_enabled.js.erb new file mode 100644 index 000000000..8e2a0eadc --- /dev/null +++ b/app/views/admin/tenants/toggle_enabled.js.erb @@ -0,0 +1,4 @@ +var replacement = $("<%= j render Admin::Tenants::RowComponent.new(@tenant) %>"); +var row = $("#<%= dom_id(@tenant) %>"); + +row.html(replacement.html()).find(".toggle-switch [type='submit']").focus(); diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 4f59150ad..b128ec8cf 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -1635,10 +1635,16 @@ en: form: use_subdomain: "Use a subdomain in the %{domain} domain to access this tenant" use_domain: "Use a different domain to access this tenant" + hide: + notice: Tenant disabled successfully index: create: Create tenant + enable: "Enable tenant %{tenant}" + enabled: Enabled new: title: New tenant + restore: + notice: Tenant enabled successfully update: notice: Tenant updated successfully homepage: diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 948c6cf5c..509748e57 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -1634,10 +1634,16 @@ es: form: use_subdomain: "Utiliza un subdominio en el dominio %{domain} para acceder a esta entidad" use_domain: "Utiliza un dominio distinto para acceder a esta entidad" + hide: + notice: Entidad deshabilitada correctamente index: create: Crear entidad + enable: "Habilitar entidad %{tenant}" + enabled: Habilitada new: title: Nueva entidad + restore: + notice: Entidad habilitada correctamente update: notice: Entidad actualizada correctamente homepage: diff --git a/config/routes/admin.rb b/config/routes/admin.rb index af7d96cec..2fa509e36 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -284,7 +284,12 @@ namespace :admin do delete :cancel, on: :collection end - resources :tenants, except: [:show, :destroy] + resources :tenants, except: [:show, :destroy] do + member do + put :hide + put :restore + end + end end resolve "Milestone" do |milestone| diff --git a/db/migrate/20221203140136_add_hidden_at_to_tenants.rb b/db/migrate/20221203140136_add_hidden_at_to_tenants.rb new file mode 100644 index 000000000..5e2006313 --- /dev/null +++ b/db/migrate/20221203140136_add_hidden_at_to_tenants.rb @@ -0,0 +1,5 @@ +class AddHiddenAtToTenants < ActiveRecord::Migration[6.0] + def change + add_column :tenants, :hidden_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index f12f53061..be20fc451 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_11_20_123254) do +ActiveRecord::Schema.define(version: 2022_12_03_140136) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -1562,6 +1562,7 @@ ActiveRecord::Schema.define(version: 2022_11_20_123254) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "schema_type", default: 0, null: false + t.datetime "hidden_at" t.index ["name"], name: "index_tenants_on_name", unique: true t.index ["schema"], name: "index_tenants_on_schema", unique: true end diff --git a/spec/models/abilities/administrator_spec.rb b/spec/models/abilities/administrator_spec.rb index a34ec08e6..956fed48c 100644 --- a/spec/models/abilities/administrator_spec.rb +++ b/spec/models/abilities/administrator_spec.rb @@ -181,6 +181,8 @@ describe Abilities::Administrator do it { should be_able_to :create, Tenant } it { should be_able_to :read, Tenant } it { should be_able_to :update, Tenant } + it { should be_able_to :hide, Tenant } + it { should be_able_to :restore, Tenant } it { should_not be_able_to :destroy, Tenant } context "administrators from other tenants" do @@ -193,6 +195,8 @@ describe Abilities::Administrator do it { should_not be_able_to :read, Tenant } it { should_not be_able_to :update, Tenant } it { should_not be_able_to :destroy, Tenant } + it { should_not be_able_to :hide, Tenant } + it { should_not be_able_to :restore, Tenant } end end end diff --git a/spec/models/tenant_spec.rb b/spec/models/tenant_spec.rb index b15dc7560..ba94f001a 100644 --- a/spec/models/tenant_spec.rb +++ b/spec/models/tenant_spec.rb @@ -117,6 +117,37 @@ describe Tenant do expect(Tenant.resolve_host("www.consul.dev")).to eq "www.consul.dev" end + it "raises an exception when accessing a hidden tenant using a subdomain" do + insert(:tenant, schema: "saturn", hidden_at: Time.current) + + expect { Tenant.resolve_host("saturn.consul.dev") }.to raise_exception(Apartment::TenantNotFound) + end + + it "raises an exception when accessing a hidden tenant using a domain" do + insert(:tenant, :domain, schema: "consul.dev", hidden_at: Time.current) + + expect { Tenant.resolve_host("consul.dev") }.to raise_exception(Apartment::TenantNotFound) + end + + it "raises an exception when accessing a hidden tenant using a domain starting with www" do + insert(:tenant, :domain, schema: "www.consul.dev", hidden_at: Time.current) + + expect { Tenant.resolve_host("www.consul.dev") }.to raise_exception(Apartment::TenantNotFound) + end + + it "raises an exception when accessing a hidden tenant with a domain and another tenant resolves to the same domain" do + insert(:tenant, :domain, schema: "saturn.consul.dev", hidden_at: Time.current) + insert(:tenant, schema: "saturn") + + expect { Tenant.resolve_host("saturn.consul.dev") }.to raise_exception(Apartment::TenantNotFound) + end + + it "ignores hidden tenants with nil as their schema" do + insert(:tenant, schema: nil, hidden_at: Time.current) + + expect(Tenant.resolve_host("consul.dev")).to be nil + end + context "multitenancy disabled" do before { allow(Rails.application.config).to receive(:multitenancy).and_return(false) } @@ -318,6 +349,11 @@ describe Tenant do expect(build(:tenant, schema: "subdomainx")).not_to be_valid end + it "is not valid with the schema of an already existing hidden record" do + insert(:tenant, schema: "subdomainx", hidden_at: Time.current) + expect(build(:tenant, schema: "subdomainx")).not_to be_valid + end + it "is not valid with an excluded subdomain" do %w[mail public shared_extensions www].each do |subdomain| tenant.schema = subdomain @@ -345,6 +381,11 @@ describe Tenant do expect(build(:tenant, name: "Name X")).not_to be_valid end + it "is not valid with the name of an already existing hidden record" do + insert(:tenant, name: "Name X", hidden_at: Time.current) + expect(build(:tenant, name: "Name X")).not_to be_valid + end + context "Domain schema type" do before { tenant.schema_type = :domain } diff --git a/spec/system/admin/tenants_spec.rb b/spec/system/admin/tenants_spec.rb index 92096261b..38389dbe8 100644 --- a/spec/system/admin/tenants_spec.rb +++ b/spec/system/admin/tenants_spec.rb @@ -63,4 +63,43 @@ describe "Tenants", :admin, :seed_tenants do expect(page).to have_current_path root_path expect(page).to have_link "Sign in" end + + scenario "Hide and restore", :show_exceptions do + create(:tenant, schema: "moon", name: "Moon") + + visit admin_tenants_path + + within("tr", text: "moon") do + expect(page).to have_content "Yes" + + click_button "Enable tenant Moon" + + expect(page).to have_content "No" + expect(page).not_to have_link "moon.lvh.me" + end + + with_subdomain("moon") do + visit root_path + + expect(page).to have_title "Not found" + end + + visit admin_tenants_path + + within("tr", text: "moon") do + expect(page).to have_content "No" + + click_button "Enable tenant Moon" + + expect(page).to have_content "Yes" + expect(page).to have_link "moon.lvh.me" + end + + with_subdomain("moon") do + visit root_path + + expect(page).to have_link "Sign in" + expect(page).not_to have_title "Not found" + end + end end