尝试使用graphql-ruby中的”visibility”功能

“Visibility” 的意思是什么? (Visibility de yisi shi shenme?)

在GraphQL中,它的意思就像直译的含义那样,表示类型和字段的可见性。

可以通过不向未经许可的客户显示特定的类型或字段来进行限制。

当然,由于无法查看type或field,即使指定了type和field发送查询也无法返回值。

用例

    • 開発中のスキーマ情報を外部に公開したくないとき

 

    特定のユーザにしかスキーマ情報を知られたくないとき、使わせたくないとき

听说Gitlab也在使用的样子呢。

请把以下内容用中文释义,只需要一种选项:

https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#feature-flags

GitLab的快照中有有关API和GraphQL的特性标志的样式指南。

身份验证的区别

與身份驗證不同的是,不會向未經許可的客戶端公開類型或字段的存在。

通过使用visibility,即使未经许可的客户端执行introspection,也可以确保不返回特定的类型或字段。

这是什么行为?

让我们以一个简单的对象类型为例。

module Types
  class QueryType < Types::BaseObject
    field :first_user, Types::UserType, null: false

    def first_user
      User.first
    end
  end
end

class Types::UserType < Types::BaseObject
  field :name, String, null: false

  def name
    object.name
  end
end

接下来,使用print_schema将上述模型转换为SDL。

因为我是用rails实现的,所以我使用rails控制台。

另外,我们将省略schema类的描述。

 rails c
> printer = GraphQL::Schema::Printer.new(GraphqlTestSchema)
> puts printer.print_schema

type Query {
  firstUser: User!
}

type User {
  name: String!
}

我认为通常可以通过这种方式来查看模式定义。

然而,我想使用graphql-ruby的visibility功能,将object type完全隐藏起来。

class Types::UserType < Types::BaseObject
  field :name, String, null: false

	# 追加したメソッド
	def self.visible?(context)
    false
  end

  def name
    object.name
  end
end

请再次运行print_schema函数。

 rails c
> printer = GraphQL::Schema::Printer.new(GraphqlTestSchema)
> puts printer.print_schema

type Query {
}

随后,type User消失了,而包含该类型的查询firstUser也无法被看到了。

另外,如果按照以下方式查询,如果没有名为firstUser的查询类型,将返回错误。

 rails c
> query = "{
	  firstUser {
      name
    }
  }"
> GraphqlTestSchema.execute(query).to_h
{"errors"=>                                                                  
  [{"message"=>"Field 'firstUser' doesn't exist on type 'Query'",            
    "locations"=>[{"line"=>2, "column"=>5}],                                 
    "path"=>["query", "firstUser"],                                          
    "extensions"=>{"code"=>"undefinedField", "typeName"=>"Query", "fieldName"=>"firstUser"}}]}

实施策略

浏览这个页面后,我发现有很多种方法。

请以中文原生方式解释以下链接的内容:https://graphql-ruby.org/schema/dynamic_types.html

使用”visible?

可见性?由于 visible 可以接收上下文,因此可以通过与用户相关联的属性来判断是否可见。

class Types::UserType < Types::BaseObject
  field :name, String, null: false

	def self.visible?(context)
	  context[:current_user]&.staff?
	end

	def name
    object.name
  end
end

如果您想自定义字段的可见性,请按以下方式操作。

首先,在BaseField类中创建一个名为visible?的实例方法。

module Types
  class BaseField < GraphQL::Schema::Field
    attr_reader :visible_user

    def initialize(*args, visible_user: '', **kwargs, &block)
      super(*args, **kwargs, &block)
      @visible_user = visible_user
    end
  
    def visible?(context)
      super && case visible_user
      when 'staff'
        context[:current_user]&.staff?
      when 'admin'
        context[:current_user]&.admin?
      else
        true
      end
    end
  end
end

通过将visible_user添加到选项字段中,可以将仅限于员工属性的用户才能查看的字段。

class Types::UserType < Types::BaseObject
	# option: visible_userを追加
  field :name, String, null: false, visible_user: 'staff'

	def name
    object.name
  end
end

使用指令。

可以使用GraphQL::Schema::Directive::Flagged,使SDL中转换时可以附加@flagged。

参考链接:https://graphql-ruby.org/type_definitions/directives

请将以下内容用中文进行同义转述:

(GraphQL指令是一种可以在GraphQL查询语言中被应用的特殊构造。这些指令可以用于修改查询的行为或者对查询结果执行特定操作。GraphQL-Ruby库提供了一组内置指令,并且还支持自定义指令的添加。指令可以应用在查询字段、查询参数以及对象类型等位置。通过使用指令,我们可以在GraphQL中实现更加灵活和功能强大的查询方式。)

首先从自定义type的可见性的方法开始。

class Types::UserType < Types::BaseObject
  field :name, String, null: false

	# directiveの指定
	directive GraphQL::Schema::Directive::Flagged, by: 'staff'

	def name
    object.name
  end
end

在使用这个directive时,需要在context中添加一个flags数组。

这里根据”by”指定的值,如果存在则可查看,不存在则不可查看。

class GraphqlController < ApplicationController
  def execute
    variables = prepare_variables(params[:variables])
    query = params[:query]
    operation_name = params[:operationName]
    context = {
      current_user: current_user,
      flags: current_user&.roles #=>['staff']
    }
    result = GraphqlTestSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
(以下中略)

让我们尝试使用SDL吧。

 rails c
> printer = GraphQL::Schema::Printer.new(GraphqlTestSchema, context: {flags: ['staff']})
> puts printer.print_schema
"""
Hides this part of the schema unless the named flag is present in context[:flags]
"""                                                                      
directive @flagged(                                                      
  """                                                                    
  Flags to check for this schema member                                  
  """                                                                    
  by: [String!]!                                                         
) on ARGUMENT_DEFINITION | ENUM | ENUM_VALUE | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
                                                                         
(中略)

type User @flagged(by: ["staff"]) {
	name: String!
}

由于在User类型中添加了@flagged,因此客户端也更容易理解!

在自定义field的可见性方面,方法大体上也是相同的。

class Types::UserType < Types::BaseObject
  field :name, String, null: false, directives: { GraphQL::Schema::Directive::Flagged => { by: 'staff'} }

	def name
    object.name
  end
end

只需将directives指定为field,其他部分与type相同对吧。

 rails c
> printer = GraphQL::Schema::Printer.new(GraphqlTestSchema, context: {flags: ['staff']})
> puts printer.print_schema
"""
Hides this part of the schema unless the named flag is present in context[:flags]
"""                                                                      
directive @flagged(                                                      
  """                                                                    
  Flags to check for this schema member                                  
  """                                                                    
  by: [String!]!                                                         
) on ARGUMENT_DEFINITION | ENUM | ENUM_VALUE | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | UNION
                                                                         
(中略)

type User {
	name: String! @flagged(by: ["staff"])
}

这次给名为”field”的领域加上了”flagged”的标记。

总结

这次我尝试写了一些关于如何使用graphql-ruby处理visible的不同方法。

就我个人而言,我觉得使用directive的方法在type和field上是一致的,而且指定的方法也很简单,看起来很不错!

请一定尝试使用这个功能,因为它可以根据用户在上下文中的信息灵活定制可见性,并且在安全方面表现出色。

请查阅相关资料。

    • https://graphql-ruby.org/schema/dynamic_types.html

 

    • https://graphql-ruby.org/type_definitions/directives.html#schema-directives

 

    • https://graphql-ruby.org/authorization/visibility.html

 

    https://graphql-ruby.org/api-doc/2.0.2/GraphQL/Schema/Directive/Flagged.html
广告
将在 10 秒后关闭
bannerAds