diff options
author | Bob Van Landuyt <bob@vanlanduyt.co> | 2018-05-23 10:55:14 +0300 |
---|---|---|
committer | Bob Van Landuyt <bob@vanlanduyt.co> | 2018-06-06 11:58:54 +0300 |
commit | 9b65d4bb417fb4939289eab94487c894f0a62db6 (patch) | |
tree | 1f97b9a1bd0d722a3c3ff4e89ec13bdb7a3aec00 /lib | |
parent | c443133e779c4c508b9c6429dd4ba623d64f03f1 (diff) |
Initial setup GraphQL using graphql-ruby 1.8
- All definitions have been replaced by classes:
http://graphql-ruby.org/schema/class_based_api.html
- Authorization & Presentation have been refactored to work in the
class based system
- Loaders have been replaced by resolvers
- Times are now coersed as ISO 8601
Diffstat (limited to 'lib')
-rw-r--r-- | lib/constraints/feature_constrainer.rb | 13 | ||||
-rw-r--r-- | lib/gitlab/graphql/authorize.rb | 52 | ||||
-rw-r--r-- | lib/gitlab/graphql/authorize/instrumentation.rb | 45 | ||||
-rw-r--r-- | lib/gitlab/graphql/present.rb | 36 | ||||
-rw-r--r-- | lib/gitlab/graphql/present/instrumentation.rb | 25 |
5 files changed, 102 insertions, 69 deletions
diff --git a/lib/constraints/feature_constrainer.rb b/lib/constraints/feature_constrainer.rb new file mode 100644 index 00000000000..05d48b0f25a --- /dev/null +++ b/lib/constraints/feature_constrainer.rb @@ -0,0 +1,13 @@ +module Constraints + class FeatureConstrainer + attr_reader :feature + + def initialize(feature) + @feature = feature + end + + def matches?(_request) + Feature.enabled?(feature) + end + end +end diff --git a/lib/gitlab/graphql/authorize.rb b/lib/gitlab/graphql/authorize.rb index 6f9de8c38b7..04f25c53e49 100644 --- a/lib/gitlab/graphql/authorize.rb +++ b/lib/gitlab/graphql/authorize.rb @@ -2,55 +2,19 @@ module Gitlab module Graphql # Allow fields to declare permissions their objects must have. The field # will be set to nil unless all required permissions are present. - class Authorize - SETUP_PROC = -> (type, *args) do - type.metadata[:authorize] ||= [] - type.metadata[:authorize].concat(args) - end - - INSTRUMENT_PROC = -> (schema) do - schema.instrument(:field, new) - end + module Authorize + extend ActiveSupport::Concern - def self.register! - GraphQL::Schema.accepts_definitions(enable_authorization: INSTRUMENT_PROC) - GraphQL::Field.accepts_definitions(authorize: SETUP_PROC) + def self.use(schema_definition) + schema_definition.instrument(:field, Instrumentation.new) end - # Replace the resolver for the field with one that will only return the - # resolved object if the permissions check is successful. - # - # Collections are not supported. Apply permissions checks for those at the - # database level instead, to avoid loading superfluous data from the DB - def instrument(_type, field) - return field unless field.metadata.include?(:authorize) - - old_resolver = field.resolve_proc - - new_resolver = -> (obj, args, ctx) do - resolved_obj = old_resolver.call(obj, args, ctx) - checker = build_checker(ctx[:current_user], field.metadata[:authorize]) - - if resolved_obj.respond_to?(:then) - resolved_obj.then(&checker) - else - checker.call(resolved_obj) - end - end - - field.redefine do - resolve(new_resolver) - end + def required_permissions + @required_permissions ||= [] end - private - - def build_checker(current_user, abilities) - proc do |obj| - # Load the elements if they weren't loaded by BatchLoader yet - obj = obj.sync if obj.respond_to?(:sync) - obj if abilities.all? { |ability| Ability.allowed?(current_user, ability, obj) } - end + def authorize(*permissions) + required_permissions.concat(permissions) end end end diff --git a/lib/gitlab/graphql/authorize/instrumentation.rb b/lib/gitlab/graphql/authorize/instrumentation.rb new file mode 100644 index 00000000000..6cb8e617f62 --- /dev/null +++ b/lib/gitlab/graphql/authorize/instrumentation.rb @@ -0,0 +1,45 @@ +module Gitlab + module Graphql + module Authorize + class Instrumentation + # Replace the resolver for the field with one that will only return the + # resolved object if the permissions check is successful. + # + # Collections are not supported. Apply permissions checks for those at the + # database level instead, to avoid loading superfluous data from the DB + def instrument(_type, field) + field_definition = field.metadata[:type_class] + return field unless field_definition.respond_to?(:required_permissions) + return field if field_definition.required_permissions.empty? + + old_resolver = field.resolve_proc + + new_resolver = -> (obj, args, ctx) do + resolved_obj = old_resolver.call(obj, args, ctx) + checker = build_checker(ctx[:current_user], field_definition.required_permissions) + + if resolved_obj.respond_to?(:then) + resolved_obj.then(&checker) + else + checker.call(resolved_obj) + end + end + + field.redefine do + resolve(new_resolver) + end + end + + private + + def build_checker(current_user, abilities) + proc do |obj| + # Load the elements if they weren't loaded by BatchLoader yet + obj = obj.sync if obj.respond_to?(:sync) + obj if abilities.all? { |ability| Ability.allowed?(current_user, ability, obj) } + end + end + end + end + end +end diff --git a/lib/gitlab/graphql/present.rb b/lib/gitlab/graphql/present.rb index b060692b334..2c7b64f1be9 100644 --- a/lib/gitlab/graphql/present.rb +++ b/lib/gitlab/graphql/present.rb @@ -1,34 +1,20 @@ module Gitlab module Graphql - class Present - PRESENT_USING = -> (type, presenter_class, *args) do - type.metadata[:presenter_class] = presenter_class - end - - INSTRUMENT_PROC = -> (schema) do - schema.instrument(:field, new) - end - - def self.register! - GraphQL::Schema.accepts_definitions(enable_presenting: INSTRUMENT_PROC) - GraphQL::ObjectType.accepts_definitions(present_using: PRESENT_USING) - end - - def instrument(type, field) - return field unless type.metadata[:presenter_class] - - old_resolver = field.resolve_proc - - resolve_with_presenter = -> (obj, args, context) do - presenter = type.metadata[:presenter_class].new(obj, **context.to_h) - - old_resolver.call(presenter, args, context) + module Present + extend ActiveSupport::Concern + prepended do + def self.present_using(kls) + @presenter_class = kls end - field.redefine do - resolve(resolve_with_presenter) + def self.presenter_class + @presenter_class end end + + def self.use(schema_definition) + schema_definition.instrument(:field, Instrumentation.new) + end end end end diff --git a/lib/gitlab/graphql/present/instrumentation.rb b/lib/gitlab/graphql/present/instrumentation.rb new file mode 100644 index 00000000000..1688262974b --- /dev/null +++ b/lib/gitlab/graphql/present/instrumentation.rb @@ -0,0 +1,25 @@ +module Gitlab + module Graphql + module Present + class Instrumentation + def instrument(type, field) + presented_in = field.metadata[:type_class].owner + return field unless presented_in.respond_to?(:presenter_class) + return field unless presented_in.presenter_class + + old_resolver = field.resolve_proc + + resolve_with_presenter = -> (presented_type, args, context) do + object = presented_type.object + presenter = presented_in.presenter_class.new(object, **context.to_h) + old_resolver.call(presenter, args, context) + end + + field.redefine do + resolve(resolve_with_presenter) + end + end + end + end + end +end |