-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathobject.rb
More file actions
145 lines (128 loc) · 5.37 KB
/
object.rb
File metadata and controls
145 lines (128 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# frozen_string_literal: true
require "graphql/query/null_context"
module GraphQL
class Schema
class Object < GraphQL::Schema::Member
extend GraphQL::Schema::Member::HasFields
extend GraphQL::Schema::Member::HasInterfaces
include Member::HasDataloader
# Raised when an Object doesn't have any field defined and hasn't explicitly opted out of this requirement
class FieldsAreRequiredError < GraphQL::Error
def initialize(object_type)
message = "Object types must have fields, but #{object_type.graphql_name} doesn't have any. Define a field for this type, remove it from your schema, or add `has_no_fields(true)` to its definition."
super(message)
end
end
# @return [Object] the application object this type is wrapping
attr_reader :object
# @return [GraphQL::Query::Context] the context instance for this query
attr_reader :context
# @return [GraphQL::Dataloader]
def dataloader
context.dataloader
end
# Call this in a field method to return a value that should be returned to the client
# without any further handling by GraphQL.
def raw_value(obj)
GraphQL::Execution::Interpreter::RawValue.new(obj)
end
class << self
# This is protected so that we can be sure callers use the public method, {.authorized_new}
# @see authorized_new to make instances
protected :new
def wrap_scoped(object, context)
scoped_new(object, context)
end
# This is called by the runtime to return an object to call methods on.
def wrap(object, context)
authorized_new(object, context)
end
# Make a new instance of this type _if_ the auth check passes,
# otherwise, raise an error.
#
# Probably only the framework should call this method.
#
# This might return a {GraphQL::Execution::Lazy} if the user-provided `.authorized?`
# hook returns some lazy value (like a Promise).
#
# The reason that the auth check is in this wrapper method instead of {.new} is because
# of how it might return a Promise. It would be weird if `.new` returned a promise;
# It would be a headache to try to maintain Promise-y state inside a {Schema::Object}
# instance. So, hopefully this wrapper method will do the job.
#
# @param object [Object] The thing wrapped by this object
# @param context [GraphQL::Query::Context]
# @return [GraphQL::Schema::Object, GraphQL::Execution::Lazy]
# @raise [GraphQL::UnauthorizedError] if the user-provided hook returns `false`
def authorized_new(object, context)
context.query.current_trace.begin_authorized(self, object, context)
begin
maybe_lazy_auth_val = context.query.current_trace.authorized(query: context.query, type: self, object: object) do
begin
authorized?(object, context)
rescue GraphQL::UnauthorizedError => err
context.schema.unauthorized_object(err)
rescue StandardError => err
context.query.handle_or_reraise(err)
end
end
ensure
context.query.current_trace.end_authorized(self, object, context, maybe_lazy_auth_val)
end
auth_val = if context.schema.lazy?(maybe_lazy_auth_val)
GraphQL::Execution::Lazy.new do
context.query.current_trace.begin_authorized(self, object, context)
context.query.current_trace.authorized_lazy(query: context.query, type: self, object: object) do
res = context.schema.sync_lazy(maybe_lazy_auth_val)
context.query.current_trace.end_authorized(self, object, context, res)
res
end
end
else
maybe_lazy_auth_val
end
context.query.after_lazy(auth_val) do |is_authorized|
if is_authorized
self.new(object, context)
else
# It failed the authorization check, so go to the schema's authorized object hook
err = GraphQL::UnauthorizedError.new(object: object, type: self, context: context)
# If a new value was returned, wrap that instead of the original value
begin
new_obj = context.schema.unauthorized_object(err)
if new_obj
self.new(new_obj, context)
else
nil
end
end
end
end
end
def scoped_new(object, context)
self.new(object, context)
end
end
def initialize(object, context)
@object = object
@context = context
end
class << self
# Set up a type-specific invalid null error to use when this object's non-null fields wrongly return `nil`.
# It should help with debugging and bug tracker integrations.
def const_missing(name)
if name == :InvalidNullError
custom_err_class = GraphQL::InvalidNullError.subclass_for(self)
const_set(:InvalidNullError, custom_err_class)
custom_err_class
else
super
end
end
def kind
GraphQL::TypeKinds::OBJECT
end
end
end
end
end