-
Notifications
You must be signed in to change notification settings - Fork 126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extend query interface #815
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
module Pageflow | ||
# Query accounts for members, e.g. based on role | ||
class AccountMemberQuery < ApplicationQuery | ||
class Scope < ApplicationQuery::Scope | ||
# Account whose members we scope | ||
# @return [Pageflow::Account] | ||
attr_reader :account | ||
|
||
# Base scope which is further scoped according to account role | ||
# @return [ActiveRecord::Relation<User>] | ||
attr_reader :scope | ||
|
||
# Create scope that can limit base scope to account members at | ||
# or above a given role | ||
# | ||
# @param [Pageflow::Account] account | ||
# Required. Membership account to check. | ||
# @param [ActiveRecord::Relation<User>] scope | ||
# Optional. Membership entity to check. | ||
def initialize(account, scope = User.all) | ||
@account = account | ||
@scope = scope | ||
end | ||
|
||
# Scope to those members from scope on account who have at least | ||
# role | ||
# | ||
# @param [String] role | ||
# Required. Minimum role that we compare against. | ||
# @return [ActiveRecord::Relation<User>] | ||
def with_role_at_least(role) | ||
scope.joins(memberships_for_account_with_at_least_role(role)) | ||
.where(membership_is_present) | ||
end | ||
|
||
private | ||
|
||
def memberships_for_account_with_at_least_role(role) | ||
options = {roles: Roles.at_least(role), account_id: account.id} | ||
|
||
sanitize_sql(<<-SQL, options) | ||
LEFT OUTER JOIN pageflow_memberships as | ||
pageflow_account_memberships ON | ||
pageflow_account_memberships.user_id = users.id AND | ||
pageflow_account_memberships.role IN (:roles) AND | ||
pageflow_account_memberships.entity_id = :account_id AND | ||
pageflow_account_memberships.entity_type = 'Pageflow::Account' | ||
SQL | ||
end | ||
|
||
def membership_is_present | ||
'pageflow_account_memberships.entity_id IS NOT NULL' | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
module Pageflow | ||
# Query users for their role on accounts based on role | ||
class AccountRoleQuery | ||
# Create query that can be used for role comparisons | ||
# | ||
# @param [User] user | ||
# Required. Membership user to check. | ||
# @param [Pageflow::Account] account | ||
# Required. Membership entity to check. | ||
def initialize(user, account) | ||
@user = user | ||
@account = account | ||
end | ||
|
||
# Return true if there is a membership with at least role for | ||
# user/account | ||
# | ||
# @param [String] role | ||
# Required. Minimum role that we compare against. | ||
# @return [Boolean] | ||
def has_at_least_role?(role) | ||
@user | ||
.memberships | ||
.where(role: Roles.at_least(role)) | ||
.where('(entity_id = :account_id AND '\ | ||
"entity_type = 'Pageflow::Account')", | ||
account_id: @account.id) | ||
.any? | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module Pageflow | ||
# Abstraction layer for Pageflow's query interface | ||
class ApplicationQuery | ||
class Scope | ||
protected | ||
|
||
# @api private | ||
def sanitize_sql(sql, interpolations) | ||
ActiveRecord::Base.send(:sanitize_sql_array, [sql, interpolations]) | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
require 'spec_helper' | ||
|
||
module Pageflow | ||
describe AccountMemberQuery do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Block has too many lines. [42/25] |
||
describe AccountMemberQuery::Scope do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Block has too many lines. [40/25] |
||
describe '.with_role_at_least' do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Block has too many lines. [38/25] |
||
it 'includes members with account membership with required role' do | ||
user = create(:user) | ||
account = create(:account, with_previewer: user) | ||
|
||
result = AccountMemberQuery::Scope.new(account) | ||
.with_role_at_least(:previewer) | ||
|
||
expect(result).to include(user) | ||
end | ||
|
||
it 'includes members with account membership with stronger role' do | ||
user = create(:user) | ||
account = create(:account, with_editor: user) | ||
|
||
result = AccountMemberQuery::Scope.new(account) | ||
.with_role_at_least(:previewer) | ||
|
||
expect(result).to include(user) | ||
end | ||
|
||
it 'does not include members with account membership with '\ | ||
'insufficient role' do | ||
user = create(:user) | ||
account = create(:account, with_member: user) | ||
|
||
result = AccountMemberQuery::Scope.new(account) | ||
.with_role_at_least(:previewer) | ||
|
||
expect(result).not_to include(user) | ||
end | ||
|
||
it 'does not include members with required role on other account' do | ||
user = create(:user) | ||
account = create(:account, with_member: user) | ||
create(:account, with_previewer: user) | ||
|
||
result = AccountMemberQuery::Scope.new(account) | ||
.with_role_at_least(:previewer) | ||
|
||
expect(result).not_to include(user) | ||
end | ||
|
||
it 'does not include members with required role on entry of account' do | ||
user = create(:user) | ||
account = create(:account, with_member: user) | ||
create(:entry, with_previewer: user) | ||
|
||
result = AccountMemberQuery::Scope.new(account) | ||
.with_role_at_least(:previewer) | ||
|
||
expect(result).not_to include(user) | ||
end | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
require 'spec_helper' | ||
|
||
module Pageflow | ||
describe AccountRoleQuery do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Block has too many lines. [41/25] |
||
describe '.has_at_least_role?' do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Block has too many lines. [39/25] |
||
it 'returns true if user has account membership with given role' do | ||
user = create(:user) | ||
account = create(:account, with_publisher: user) | ||
|
||
result = AccountRoleQuery.new(user, account) | ||
.has_at_least_role?(:publisher) | ||
|
||
expect(result).to be true | ||
end | ||
|
||
it 'returns true if user has account membership with stronger role' do | ||
user = create(:user) | ||
account = create(:account, with_manager: user) | ||
|
||
result = AccountRoleQuery.new(user, account) | ||
.has_at_least_role?(:publisher) | ||
|
||
expect(result).to be true | ||
end | ||
|
||
it 'returns false if user has account membership with weaker role' do | ||
user = create(:user) | ||
account = create(:account, with_editor: user) | ||
|
||
result = AccountRoleQuery.new(user, account) | ||
.has_at_least_role?(:publisher) | ||
|
||
expect(result).to be false | ||
end | ||
|
||
it 'returns false if user has entry membership with given role on '\ | ||
'account' do | ||
user = create(:user) | ||
account = create(:account, with_editor: user) | ||
create(:entry, with_publisher: user) | ||
|
||
result = AccountRoleQuery.new(user, account) | ||
.has_at_least_role?(:publisher) | ||
|
||
expect(result).to be false | ||
end | ||
|
||
it 'returns false if user has account membership with given role on '\ | ||
'other account' do | ||
user = create(:user) | ||
account = create(:account, with_editor: user) | ||
create(:account, with_publisher: user) | ||
|
||
result = AccountRoleQuery.new(user, account) | ||
.has_at_least_role?(:publisher) | ||
|
||
expect(result).to be false | ||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think,
FolderPolicy
could also benefit from using the query. Please check if some these_or_above
methods onMembership
are unused then and can be removed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EDIT: Disregard the following two comments. I think adding a Scope is the way to go here:
Would this be okay in principle? Would we want to catch the error/throw something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(We would need
on_as_many_accounts_as: 2
for now, but doing it for1
and2
only seems excessively lazy)Also, the other methods are supposed to be private, so the ambiguous naming is okay because of context and because long method names impede reading
Specific usage:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding the Scope requires specs and maybe thought, so I will leave it for the next apparent opportunity. I agree, however, that we can probably use the extended query interface in other policies as well and thus improve readability, design and such