Skip to content

Commit

Permalink
Allow users to have additional organisations
Browse files Browse the repository at this point in the history
This creates a `has_and_belongs_to_many` join table relationship between
organisations and users, allowing a user to belong to
`additional_organisations` and paves the way to allowing a user to set their
`organisation` to be a `Current.organisation` and thus view data for that
organisation instead of their own "real" organisation.

This should change nothing about the existing functionality of the site and
will be entirely invisible to end users. (The next phase will include
front-end changes which *will* allow selected users to switch their current
organisation.)

Helper methods on the `User` model to navigate this relationship include
`primary_organisation` (to retrieve the user's actual `organisation`, which
is necessarily overwritten), `all_organisations` (the union of
`primary_organisation` and `additional_organisations`) and
`additional_organisations?` (to determine whether a user has a non-zero
number of `additional_organisations`).
  • Loading branch information
benshimmin committed Dec 5, 2024
1 parent 2697408 commit bb08a6d
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 1 deletion.
3 changes: 3 additions & 0 deletions app/models/current.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Current < ActiveSupport::CurrentAttributes
attribute :organisation
end
20 changes: 20 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ class User < ApplicationRecord
otp_secret_encryption_key: ENV["SECRET_KEY_BASE"]

belongs_to :organisation
has_and_belongs_to_many :additional_organisations, class_name: "Organisation", join_table: "organisations_users"
has_many :historical_events
validates_presence_of :name, :email
validates :email, with: :email_cannot_be_changed_after_create, on: :update
Expand All @@ -18,6 +19,25 @@ class User < ApplicationRecord

delegate :service_owner?, :partner_organisation?, to: :organisation

def organisation
if Current.organisation
return Organisation.find(Current.organisation)
end
super
end

def primary_organisation
Organisation.find(organisation_id)
end

def all_organisations
Organisation.where(id: [organisation_id, additional_organisations.map(&:id)].flatten)
end

def additional_organisations?
additional_organisations.any?
end

def active_for_authentication?
active
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class CreateJoinTableOrganisationsUsers < ActiveRecord::Migration[6.1]
def change
create_join_table :organisations, :users, column_options: {type: :uuid} do |t|
t.index [:organisation_id, :user_id]
t.index [:user_id, :organisation_id]
end
end
end
9 changes: 8 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_11_14_114012) do
ActiveRecord::Schema.define(version: 2024_12_04_220209) do

# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
Expand Down Expand Up @@ -286,6 +286,13 @@
t.index ["iati_reference"], name: "index_organisations_on_iati_reference", unique: true
end

create_table "organisations_users", id: false, force: :cascade do |t|
t.uuid "organisation_id", null: false
t.uuid "user_id", null: false
t.index ["organisation_id", "user_id"], name: "index_organisations_users_on_organisation_id_and_user_id"
t.index ["user_id", "organisation_id"], name: "index_organisations_users_on_user_id_and_organisation_id"
end

create_table "outgoing_transfers", id: :uuid, default: -> { "public.gen_random_uuid()" }, force: :cascade do |t|
t.uuid "source_id", null: false
t.uuid "destination_id", null: false
Expand Down
64 changes: 64 additions & 0 deletions spec/models/user_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,70 @@
# This also validates that the relationship is present
it { is_expected.to belong_to(:organisation) }
it { is_expected.to have_many(:historical_events) }
it { is_expected.to have_and_belong_to_many(:additional_organisations) }

it "has a primary organisation" do
user = create(:administrator)

expect(user.primary_organisation).to be_valid
expect(user.primary_organisation.id).to eq(user.organisation_id)
end

it "has additional organisations" do
org1 = create(:partner_organisation)
org2 = create(:partner_organisation)
org3 = create(:partner_organisation)

user = create(:administrator)
user.additional_organisations << [org1, org2, org3]

expect(user.additional_organisations.pluck(:id)).to include(org1.id)
expect(user.additional_organisations.count).to eq(3)
end

it "shows all organisations including the primary organisation" do
org = create(:partner_organisation)

user = create(:administrator)
user.additional_organisations << org

expect(user.all_organisations.size).to eq(2)
expect(user.all_organisations.pluck(:id)).to include(user.primary_organisation.id)
expect(user.all_organisations.pluck(:id)).to include(org.id)
end

it "determines whether there are additional organisations" do
org = create(:partner_organisation)

user = create(:administrator)
user.additional_organisations << org

expect(user.additional_organisations?).to eq(true)

user.additional_organisations = []
expect(user.additional_organisations?).to eq(false)
end

context "when the current organisation has been set" do
let(:current_organisation) do
create(:partner_organisation)
end

before do
Current.organisation = current_organisation.id
end

it "returns the current organisation instead of the primary organisation" do
user = create(:administrator)

expect(user.organisation).to eq(current_organisation)
expect(user.primary_organisation).not_to eq(current_organisation)
end

after do
Current.organisation = nil
end
end
end

describe "delegations" do
Expand Down

0 comments on commit bb08a6d

Please sign in to comment.