Skip to content

Commit

Permalink
Start visible_in
Browse files Browse the repository at this point in the history
  • Loading branch information
rmosolgo committed Aug 26, 2024
1 parent fcfed4c commit 5830fb6
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 6 deletions.
5 changes: 5 additions & 0 deletions lib/graphql/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ def initialize(schema, query_string = nil, query: nil, document: nil, context: n
use_schema_subset = warden ? false : schema.use_schema_visibility?
end

@visibility_profile = visibility_profile

if use_schema_subset
@schema_subset = @schema.visibility.profile_for(@context, visibility_profile)
@warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema)
Expand Down Expand Up @@ -188,6 +190,9 @@ def query_string
@query_string ||= (document ? document.to_query_string : nil)
end

# @return [Symbol, nil]
attr_reader :visibility_profile

attr_accessor :multiplex

# @return [GraphQL::Tracing::Trace]
Expand Down
3 changes: 3 additions & 0 deletions lib/graphql/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,9 @@ def static_validator
GraphQL::StaticValidation::Validator.new(schema: self)
end

# Add `plugin` to this schema
# @param plugin [#use] A Schema plugin
# @return void
def use(plugin, **kwargs)
if kwargs.any?
plugin.use(self, **kwargs)
Expand Down
57 changes: 57 additions & 0 deletions lib/graphql/schema/visibility.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def initialize(schema, dynamic:, preload:, profiles:)

if preload
profiles.each do |profile_name, example_ctx|
example_ctx ||= { visibility_profile: profile_name }
prof = profile_for(example_ctx, profile_name)
prof.all_types # force loading
end
Expand All @@ -52,6 +53,62 @@ def profile_for(context, visibility_profile)
Subset.new(context: context, schema: @schema)
end
end

module TypeIntegration
def self.included(child_cls)
child_cls.extend(ClassMethods)
end

module ClassMethods
def visible_in(profiles = NOT_CONFIGURED)
if NOT_CONFIGURED.equal?(profiles)
@visible_in
else
@visible_in = Array(profiles)
end
end

# TODO visible?

def inherited(child_cls)
super
if visible_in
child_cls.visible_in(visible_in)
else
child_cls.visible_in(nil)
end
end
end
end
module FieldIntegration
def self.included(child_cls)
child_cls.extend(ClassMethods)
end

module ClassMethods
def visible_in(visible_in = NOT_CONFIGURED)
if NOT_CONFIGURED.equal?(visible_in)
@visible_in
else
@visible_in = Array(visible_in)
end
end
end
def initialize(*args, visible_in: nil, **kwargs, &block)
@visible_in = visible_in ? Array(visible_in) : nil
super(*args, **kwargs, &block)
end

def visible?(context)
v_i = @visible_in || self.class.visible_in
if v_i
v_p = context.respond_to?(:query) ? context.query.visibility_profile : context[:visibility_profile]
super && v_i.include?(v_p)
else
super
end
end
end
end
end
end
1 change: 1 addition & 0 deletions spec/graphql/schema/object_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ def self.type_error(err, ctx)
shape.delete(:@configs)
shape.delete(:@future_schema)
shape.delete(:@metadata)
shape.delete(:@admin_only)
if type_defn_shapes.add?(shape)
example_shapes_by_name[cls.graphql_name] = shape
end
Expand Down
47 changes: 41 additions & 6 deletions spec/graphql/schema/visibility_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,22 @@ class Product < BaseObject
field :name, String
field :price, Integer
field :cost_of_goods_sold, Integer, admin_only: true
field :quantity_in_stock, Integer
end

class Query < BaseObject
field :products, [Product]

def products
[{ name: "Pool Noodle", price: 100, cost_of_goods_sold: 5, quantity_in_stock: 100 }]
[{ name: "Pool Noodle", price: 100, cost_of_goods_sold: 5 }]
end
end

query(Query)
use GraphQL::Schema::Visibility, profiles: { public: {}, admin: { is_admin: true }, edge: {} }, preload: true
use GraphQL::Schema::Visibility, profiles: { public: {}, admin: { is_admin: true } }, preload: true
end

class DynVisSchema < VisSchema
use GraphQL::Schema::Visibility, profiles: { public: {}, admin: {}, edge: {} }, dynamic: true, preload: false
use GraphQL::Schema::Visibility, profiles: { public: {}, admin: {} }, dynamic: true, preload: false
end

def exec_query(...)
Expand All @@ -61,7 +60,7 @@ def exec_query(...)
err = assert_raises ArgumentError do
exec_query("{ products { name } }", visibility_profile: :nonsense )
end
expected_msg = "`:nonsense` isn't allowed for `visibility_profile:` (must be one of :public, :admin, :edge). Or, add `:nonsense` to the list of profiles in the schema definition."
expected_msg = "`:nonsense` isn't allowed for `visibility_profile:` (must be one of :public, :admin). Or, add `:nonsense` to the list of profiles in the schema definition."
assert_equal expected_msg, err.message
end

Expand All @@ -88,8 +87,44 @@ def exec_query(...)

describe "preloading profiles" do
it "preloads when true" do
assert_equal [:public, :admin, :edge], VisSchema.visibility.cached_profiles.keys, "preload: true"
assert_equal [:public, :admin], VisSchema.visibility.cached_profiles.keys, "preload: true"
assert_equal 0, DynVisSchema.visibility.cached_profiles.size, "preload: false"
end
end

describe "configuring named profiles" do
class NamedVisSchema < GraphQL::Schema
class BaseField < GraphQL::Schema::Field
include GraphQL::Schema::Visibility::FieldIntegration
visible_in([:public, :admin])
end
class BaseObject < GraphQL::Schema::Object
include GraphQL::Schema::Visibility::TypeIntegration
field_class(BaseField)
visible_in([:public, :admin])
end
class Query < BaseObject
field :i, Int, fallback_value: 1
field :i2, Int, fallback_value: 2, visible_in: :admin
end

query(Query)
use GraphQL::Schema::Visibility, profiles: [:public, :admin]
end

it "runs queries by named profile" do
res = NamedVisSchema.execute("{ i }", visibility_profile: :public)
assert_equal 1, res["data"]["i"]

res = NamedVisSchema.execute("{ i2 }", visibility_profile: :admin)
assert_equal 2, res["data"]["i2"]

res = NamedVisSchema.execute("{ i i2 }", visibility_profile: :public)
assert_equal ["Field 'i2' doesn't exist on type 'Query'"], res["errors"].map { |e| e["message"] }

res = NamedVisSchema.execute("{ i i2 }", visibility_profile: :admin)
assert_equal 1, res["data"]["i"]
assert_equal 2, res["data"]["i2"]
end
end
end

0 comments on commit 5830fb6

Please sign in to comment.