Simplify the way GraphQL 'resolve' is used
This commit is contained in:
@@ -28,7 +28,7 @@ class Comment < ActiveRecord::Base
|
||||
scope :not_as_admin_or_moderator, -> { where("administrator_id IS NULL").where("moderator_id IS NULL")}
|
||||
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
|
||||
|
||||
scope :public_for_api, -> do
|
||||
def self.public_for_api
|
||||
joins("FULL OUTER JOIN debates ON commentable_type = 'Debate' AND commentable_id = debates.id").
|
||||
joins("FULL OUTER JOIN proposals ON commentable_type = 'Proposal' AND commentable_id = proposals.id").
|
||||
where("commentable_type = 'Proposal' AND proposals.hidden_at IS NULL OR commentable_type = 'Debate' AND debates.hidden_at IS NULL")
|
||||
|
||||
@@ -7,7 +7,9 @@ class ProposalNotification < ActiveRecord::Base
|
||||
validates :proposal, presence: true
|
||||
validate :minimum_interval
|
||||
|
||||
scope :public_for_api, -> { joins(:proposal).where("proposals.hidden_at IS NULL") }
|
||||
def self.public_for_api
|
||||
joins(:proposal).where("proposals.hidden_at IS NULL")
|
||||
end
|
||||
|
||||
def minimum_interval
|
||||
return true if proposal.try(:notifications).blank?
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
class Tag < ActsAsTaggableOn::Tag
|
||||
scope :public_for_api, -> { where("kind IS NULL OR kind = 'category'") }
|
||||
|
||||
def self.public_for_api
|
||||
where("kind IS NULL OR kind = 'category'")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ class Vote < ActsAsVotable::Vote
|
||||
scope :for_proposals, -> { where(votable_type: 'Proposal') }
|
||||
scope :for_comments, -> { where(votable_type: 'Comment') }
|
||||
|
||||
scope :public_for_api, -> do
|
||||
def self.public_for_api
|
||||
joins("FULL OUTER JOIN debates ON votable_type = 'Debate' AND votable_id = debates.id").
|
||||
joins("FULL OUTER JOIN proposals ON votable_type = 'Proposal' AND votable_id = proposals.id").
|
||||
joins("FULL OUTER JOIN comments ON votable_type = 'Comment' AND votable_id = comments.id").
|
||||
|
||||
1
config/initializers/active_record_extensions.rb
Normal file
1
config/initializers/active_record_extensions.rb
Normal file
@@ -0,0 +1 @@
|
||||
require 'active_record_extensions'
|
||||
12
lib/active_record_extensions.rb
Normal file
12
lib/active_record_extensions.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
module PublicForApi
|
||||
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
def public_for_api
|
||||
all
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.send(:include, PublicForApi)
|
||||
@@ -49,12 +49,12 @@ module GraphQL
|
||||
field(field_name, SCALAR_TYPES[field_type])
|
||||
when :simple_association
|
||||
field(field_name, -> { api_types_creator.created_types[field_type] }) do
|
||||
resolve GraphQL::AssociationResolver.new(field_name, field_type)
|
||||
resolve -> (object, arguments, context) { field_type.public_for_api.find(object) }
|
||||
end
|
||||
when :paginated_association
|
||||
field_type = field_type.first
|
||||
connection(field_name, -> { api_types_creator.created_types[field_type].connection_type }) do
|
||||
resolve GraphQL::AssociationResolver.new(field_name, field_type)
|
||||
resolve -> (object, arguments, context) { field_type.public_for_api }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
module GraphQL
|
||||
class AssociationResolver
|
||||
attr_reader :field_name, :target_model, :allowed_elements
|
||||
|
||||
def initialize(field_name, target_model)
|
||||
@field_name = field_name
|
||||
@target_model = target_model
|
||||
@allowed_elements = target_public_elements
|
||||
end
|
||||
|
||||
def call(object, arguments, context)
|
||||
requested_elements = object.send(field_name)
|
||||
filter_forbidden_elements(requested_elements)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def target_public_elements
|
||||
target_model.respond_to?(:public_for_api) ? target_model.public_for_api : target_model.all
|
||||
end
|
||||
|
||||
def filter_forbidden_elements(requested_elements)
|
||||
if requested_elements.respond_to?(:each)
|
||||
requested_elements.all & allowed_elements.all
|
||||
else
|
||||
allowed_elements.include?(requested_elements) ? requested_elements : nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -22,13 +22,13 @@ module GraphQL
|
||||
type created_type
|
||||
description "Find one #{model.model_name.human} by ID"
|
||||
argument :id, !types.ID
|
||||
resolve GraphQL::RootElementResolver.new(model)
|
||||
resolve -> (object, arguments, context) { model.public_for_api.find_by(id: arguments['id'])}
|
||||
end
|
||||
end
|
||||
|
||||
connection model.name.underscore.pluralize.to_sym, created_type.connection_type do
|
||||
description "Find all #{model.model_name.human.pluralize}"
|
||||
resolve GraphQL::RootCollectionResolver.new(model)
|
||||
resolve -> (object, arguments, context) { model.public_for_api }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
module GraphQL
|
||||
class RootCollectionResolver
|
||||
attr_reader :target_model
|
||||
|
||||
def initialize(target_model)
|
||||
@target_model = target_model
|
||||
end
|
||||
|
||||
def call(object, arguments, context)
|
||||
if target_model.respond_to?(:public_for_api)
|
||||
target_model.public_for_api
|
||||
else
|
||||
target_model.all
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,24 +0,0 @@
|
||||
module GraphQL
|
||||
class RootElementResolver
|
||||
attr_reader :target_model
|
||||
|
||||
def initialize(target_model)
|
||||
@target_model = target_model
|
||||
end
|
||||
|
||||
def call(object, arguments, context)
|
||||
public_elements.find_by(id: arguments['id'])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def public_elements
|
||||
if target_model.respond_to?(:public_for_api)
|
||||
target_model.public_for_api
|
||||
else
|
||||
target_model
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,59 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe GraphQL::AssociationResolver do
|
||||
let(:comments_resolver) { GraphQL::AssociationResolver.new(:comments, Comment) }
|
||||
let(:geozone_resolver) { GraphQL::AssociationResolver.new(:geozone, Geozone) }
|
||||
let(:geozones_resolver) { GraphQL::AssociationResolver.new(:geozones, Geozone) }
|
||||
|
||||
describe '#initialize' do
|
||||
it 'sets allowed elements for unscoped models' do
|
||||
geozone_1 = create(:geozone)
|
||||
geozone_2 = create(:geozone)
|
||||
|
||||
expect(geozones_resolver.allowed_elements).to match_array([geozone_1, geozone_2])
|
||||
end
|
||||
|
||||
it 'sets allowed elements for scoped models' do
|
||||
public_comment = create(:comment, commentable: create(:proposal))
|
||||
restricted_comment = create(:comment, commentable: create(:proposal, :hidden))
|
||||
|
||||
expect(comments_resolver.allowed_elements).to match_array([public_comment])
|
||||
end
|
||||
end
|
||||
|
||||
describe '#call' do
|
||||
it 'resolves simple associations' do
|
||||
geozone = create(:geozone)
|
||||
proposal = create(:proposal, geozone: geozone)
|
||||
|
||||
result = geozone_resolver.call(proposal, nil, nil)
|
||||
|
||||
expect(result).to eq(geozone)
|
||||
end
|
||||
|
||||
it 'blocks forbidden elements when resolving simple associations' do
|
||||
skip 'None of the current models allows this spec to be executed'
|
||||
end
|
||||
|
||||
it 'resolves paginated associations' do
|
||||
proposal = create(:proposal)
|
||||
comment_1 = create(:comment, commentable: proposal)
|
||||
comment_2 = create(:comment, commentable: proposal)
|
||||
comment_3 = create(:comment, commentable: create(:proposal))
|
||||
|
||||
result = comments_resolver.call(proposal, nil, nil)
|
||||
|
||||
expect(result).to match_array([comment_1, comment_2])
|
||||
end
|
||||
|
||||
it 'blocks forbidden elements when resolving paginated associations' do
|
||||
proposal = create(:proposal, :hidden)
|
||||
comment = create(:comment, commentable: proposal)
|
||||
|
||||
result = comments_resolver.call(proposal, nil, nil)
|
||||
|
||||
expect(result).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,28 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe GraphQL::RootCollectionResolver do
|
||||
let(:geozones_resolver) { GraphQL::RootCollectionResolver.new(Geozone) }
|
||||
let(:comments_resolver) { GraphQL::RootCollectionResolver.new(Comment) }
|
||||
|
||||
describe '#call' do
|
||||
it 'returns the whole colleciton for unscoped models' do
|
||||
geozone_1 = create(:geozone)
|
||||
geozone_2 = create(:geozone)
|
||||
|
||||
result = geozones_resolver.call(nil, nil, nil)
|
||||
|
||||
expect(result).to match_array([geozone_1, geozone_2])
|
||||
end
|
||||
|
||||
it 'blocks forbidden elements for scoped models' do
|
||||
proposal = create(:proposal, :hidden)
|
||||
comment_1 = create(:comment)
|
||||
comment_2 = create(:comment, commentable: proposal)
|
||||
|
||||
result = comments_resolver.call(nil, nil, nil)
|
||||
|
||||
expect(result).to match_array([comment_1])
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,44 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe GraphQL::RootElementResolver do
|
||||
let(:comment_resolver) { GraphQL::RootElementResolver.new(Comment) }
|
||||
let(:geozone_resolver) { GraphQL::RootElementResolver.new(Geozone) }
|
||||
|
||||
describe '#call' do
|
||||
|
||||
it 'resolves simple elements' do
|
||||
comment = create(:comment)
|
||||
|
||||
result = comment_resolver.call(nil, {'id' => comment.id}, nil)
|
||||
|
||||
expect(result).to eq(comment)
|
||||
end
|
||||
|
||||
it 'returns nil when requested element is forbidden' do
|
||||
proposal = create(:proposal, :hidden)
|
||||
comment = create(:comment, commentable: proposal)
|
||||
|
||||
result = comment_resolver.call(nil, {'id' => comment.id}, nil)
|
||||
|
||||
expect(result).to be_nil
|
||||
end
|
||||
|
||||
it 'returns nil when requested element does not exist' do
|
||||
result = comment_resolver.call(nil, {'id' => 1}, nil)
|
||||
|
||||
expect(result).to be_nil
|
||||
end
|
||||
|
||||
it 'uses the public_for_api scope when available' do
|
||||
geozone = create(:geozone)
|
||||
comment = create(:comment, commentable: create(:proposal, :hidden))
|
||||
|
||||
geozone_result = geozone_resolver.call(nil, {'id' => geozone.id}, nil)
|
||||
comment_result = comment_resolver.call(nil, {'id' => comment.id}, nil)
|
||||
|
||||
expect(geozone_result).to eq(geozone)
|
||||
expect(comment_result).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user