尝试使用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