graphiti 1.2.31 → 1.2.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c8a84a85ead11c3f8cf69f4e1fb060ea7e9ce6f470b31181214c5f62aa6f847
4
- data.tar.gz: 52b0574208041cff9168e04e9e0946bbaa856c6dc22b1ea3f425673faffbffd5
3
+ metadata.gz: 9ec84a0ed1957831fa1ae6bec24a6470d5bb16189681e096b5d59a468b95b49a
4
+ data.tar.gz: 145633f22cad8f867ce295337a0b3bc5adfc45da6695b481abb092d0f7b17ed8
5
5
  SHA512:
6
- metadata.gz: 0ea059b18be0333f2193d024758de5c4a10a0f8f4e4c71d402a1b43e4d3b2f6a3fb9f426d21facf586406178d1e92398ade8f62c3c272adfdbb057623510ee54
7
- data.tar.gz: 4fb47cd7b6a9397af3660bf1996cdc6f1a33d89d21f2bc67645189665093b0268e46776d1d5871374f7198e9549f15655e5cfc8535ce1a800b6fa4be17462f66
6
+ metadata.gz: a841e2eceebba1e495608ec2037a1c7deffa408fc7e408756a6edbfed0cfaded42d82abf55899724ec201a427a6f8226bbe554142739ff1067133d9d499835bd
7
+ data.tar.gz: 3e6b741c6942c8b5ae89b08c7d5fdc6eb71b4c1b82794788207813bc914f0f245c43f196d603e2478c7dba61145403c507e1d7937028f8e217e71144beb2c128
data/CHANGELOG.md CHANGED
@@ -10,6 +10,7 @@ Features:
10
10
 
11
11
  Fixes:
12
12
  - [282] Support model names including "Resource"
13
+ - [313](https://github.com/graphiti-api/graphiti/pull/313) Sort remote resources in schema generation
13
14
 
14
15
  ## 1.1.0
15
16
 
@@ -12,6 +12,7 @@ module Graphiti
12
12
  def api_namespace
13
13
  @api_namespace ||= begin
14
14
  ns = graphiti_config["namespace"]
15
+ ns.delete_suffix("/")
15
16
 
16
17
  if ns.blank?
17
18
  ns = prompt \
@@ -34,7 +34,7 @@ module Graphiti
34
34
 
35
35
  def build_url(scope)
36
36
  url = resource.remote_url
37
- params = scope[:params].merge(scope.except(:params))
37
+ params = scope[:params].merge(scope.except(:params, :foreign_key))
38
38
  params[:page] ||= {}
39
39
  params[:page][:size] ||= 999
40
40
  params = CGI.unescape(params.to_query)
@@ -11,8 +11,16 @@ module Graphiti
11
11
  .persist_with_relationships(x[:meta], x[:attributes], x[:relationships])
12
12
  processed << x
13
13
  rescue Graphiti::Errors::RecordNotFound
14
- path = "relationships/#{x.dig(:meta, :jsonapi_type)}"
15
- raise Graphiti::Errors::RecordNotFound.new(x[:sideload].name, id, path)
14
+ if Graphiti.config.raise_on_missing_sidepost
15
+ path = "relationships/#{x.dig(:meta, :jsonapi_type)}"
16
+ raise Graphiti::Errors::RecordNotFound.new(x[:sideload].name, id, path)
17
+ else
18
+ pointer = "data/relationships/#{x.dig(:meta, :jsonapi_type)}"
19
+ object = Graphiti::Errors::NullRelation.new(id.to_s, pointer)
20
+ object.errors.add(:base, :not_found, message: "could not be found")
21
+ x[:object] = object
22
+ processed << x
23
+ end
16
24
  end
17
25
  end
18
26
  end
@@ -14,6 +14,7 @@ module Graphiti
14
14
  attr_accessor :pagination_links_on_demand
15
15
  attr_accessor :pagination_links
16
16
  attr_accessor :typecast_reads
17
+ attr_accessor :raise_on_missing_sidepost
17
18
 
18
19
  attr_reader :debug, :debug_models
19
20
 
@@ -29,6 +30,7 @@ module Graphiti
29
30
  @pagination_links_on_demand = false
30
31
  @pagination_links = false
31
32
  @typecast_reads = true
33
+ @raise_on_missing_sidepost = true
32
34
  self.debug = ENV.fetch("GRAPHITI_DEBUG", true)
33
35
  self.debug_models = ENV.fetch("GRAPHITI_DEBUG_MODELS", false)
34
36
 
@@ -43,7 +45,7 @@ module Graphiti
43
45
  end
44
46
 
45
47
  if (logger = ::Rails.logger)
46
- self.debug = logger.level.zero? && debug
48
+ self.debug = logger.debug? && debug
47
49
  Graphiti.logger = logger
48
50
  end
49
51
  end
@@ -11,6 +11,7 @@ module Graphiti
11
11
 
12
12
  def links
13
13
  @links ||= {}.tap do |links|
14
+ links[:self] = pagination_link(current_page)
14
15
  links[:first] = pagination_link(1)
15
16
  links[:last] = pagination_link(last_page)
16
17
  links[:prev] = pagination_link(current_page - 1) unless current_page == 1
@@ -2,6 +2,32 @@ module Graphiti
2
2
  module Errors
3
3
  class Base < StandardError; end
4
4
 
5
+ class UnreadableAttribute < Base
6
+ def initialize(resource_class, name)
7
+ @resource_class = resource_class
8
+ @name = name
9
+ end
10
+
11
+ def message
12
+ "#{@resource_class}: Requested field #{@name}, but not authorized to read it"
13
+ end
14
+ end
15
+
16
+ class NullRelation
17
+ extend ActiveModel::Naming
18
+ attr_accessor :id, :errors, :pointer
19
+
20
+ def initialize(id, pointer)
21
+ @id = id
22
+ @pointer = pointer
23
+ @errors = ActiveModel::Errors.new(self)
24
+ end
25
+
26
+ def self.human_attribute_name(attr, options = {})
27
+ attr
28
+ end
29
+ end
30
+
5
31
  class AdapterNotImplemented < Base
6
32
  def initialize(adapter, attribute, method)
7
33
  @adapter = adapter
@@ -1,39 +1,138 @@
1
1
  module Graphiti
2
2
  module SerializableHash
3
- def to_hash(fields: nil, include: {})
3
+ def to_hash(fields: nil, include: {}, name_chain: [], graphql: false)
4
4
  {}.tap do |hash|
5
- fields_list = fields[jsonapi_type] if fields
5
+ if fields
6
+ fields_list = nil
7
+
8
+ # Dot syntax wins over jsonapi type
9
+ if name_chain.length > 0
10
+ fields_list = fields[name_chain.join(".").to_sym]
11
+ end
12
+
13
+ if fields_list.nil?
14
+ fields_list = fields[jsonapi_type]
15
+ end
16
+ end
17
+
18
+ # polymorphic resources - merge the PARENT type
19
+ if polymorphic_subclass?
20
+ if fields[@resource.type]
21
+ fields_list ||= []
22
+ fields_list |= fields[@resource.type]
23
+ end
24
+
25
+ if fields[jsonapi_type]
26
+ fields_list ||= []
27
+ fields_list |= fields[jsonapi_type]
28
+ end
29
+ end
30
+
6
31
  attrs = requested_attributes(fields_list).each_with_object({}) { |(k, v), h|
7
- h[k] = instance_eval(&v)
32
+ name = graphql ? k.to_s.camelize(:lower).to_sym : k
33
+ h[name] = instance_eval(&v)
8
34
  }
9
- rels = @_relationships.select { |k, v| !!include[k] }
35
+
36
+ # The main logic here is just !!include[k]
37
+ # But we also have special on__<type>--<name> includes
38
+ # Where we only include when matching the polymorphic type
39
+ rels = @_relationships.select { |k, v|
40
+ if include[k]
41
+ true
42
+ else
43
+ included = false
44
+ include.keys.each do |key|
45
+ split = key.to_s.split(/^on__/)
46
+ if split.length > 1
47
+ requested_type, key = split[1].split("--")
48
+ if requested_type.to_sym == jsonapi_type
49
+ included = k == key.to_sym
50
+ break
51
+ end
52
+ end
53
+ end
54
+ included
55
+ end
56
+ }
57
+
10
58
  rels.each_with_object({}) do |(k, v), h|
59
+ nested_include = include[k]
60
+
61
+ # This logic only fires if it's a special on__<type>--<name> include
62
+ unless include.has_key?(k)
63
+ include.keys.each do |include_key|
64
+ if k == include_key.to_s.split("--")[1].to_sym
65
+ nested_include = include[include_key]
66
+ break
67
+ end
68
+ end
69
+ end
70
+
11
71
  serializers = v.send(:resources)
12
- attrs[k] = if serializers.is_a?(Array)
13
- serializers.map do |rr| # use private method to avoid array casting
14
- rr.to_hash(fields: fields, include: include[k])
72
+ name = graphql ? k.to_s.camelize(:lower) : k
73
+ name_chain = name_chain.dup
74
+ name_chain << k unless name_chain.last == k
75
+
76
+ unless remote_resource? && serializers.nil?
77
+ attrs[name.to_sym] = if serializers.is_a?(Array)
78
+ serializers.map do |rr|
79
+ rr.to_hash(fields: fields, include: nested_include, graphql: graphql, name_chain: name_chain)
80
+ end
81
+ elsif serializers.nil?
82
+ if @resource.class.respond_to?(:sideload)
83
+ if @resource.class.sideload(k).type.to_s.include?("_many")
84
+ []
85
+ end
86
+ end
87
+ else
88
+ serializers.to_hash(fields: fields, include: nested_include, graphql: graphql, name_chain: name_chain)
15
89
  end
16
- elsif serializers.nil?
17
- nil
18
- else
19
- serializers.to_hash(fields: fields, include: include[k])
20
90
  end
21
91
  end
22
92
 
23
- hash[:id] = jsonapi_id
93
+ if !graphql || (fields_list || []).include?(:id)
94
+ hash[:id] = jsonapi_id
95
+ end
96
+
97
+ if (fields_list || []).include?(:_type)
98
+ hash[:_type] = jsonapi_type.to_s
99
+ end
100
+
101
+ if (fields_list || []).include?(:__typename)
102
+ resource_class = @resource.class
103
+ if polymorphic_subclass?
104
+ resource_class = @resource.class.resource_for_type(jsonapi_type)
105
+ end
106
+ hash[:__typename] = ::Graphiti::Util::Class
107
+ .graphql_type_name(resource_class.name)
108
+ end
109
+
24
110
  hash.merge!(attrs) if attrs.any?
25
111
  end
26
112
  end
113
+
114
+ def polymorphic_subclass?
115
+ !remote_resource? &&
116
+ @resource.polymorphic? &&
117
+ @resource.type != jsonapi_type
118
+ end
119
+
120
+ # See hack in util/remote_serializer.rb
121
+ def remote_resource?
122
+ @resource == 1
123
+ end
27
124
  end
28
125
 
29
126
  class HashRenderer
30
- def initialize(resource)
127
+ def initialize(resource, graphql: false)
31
128
  @resource = resource
129
+ @graphql = graphql
32
130
  end
33
131
 
34
132
  def render(options)
35
133
  serializers = options[:data]
36
134
  opts = options.slice(:fields, :include)
135
+ opts[:graphql] = @graphql
37
136
  to_hash(serializers, opts).tap do |hash|
38
137
  hash.merge!(options.slice(:meta)) unless options[:meta].empty?
39
138
  end
@@ -43,7 +142,15 @@ module Graphiti
43
142
 
44
143
  def to_hash(serializers, opts)
45
144
  {}.tap do |hash|
46
- hash[:data] = if serializers.is_a?(Array)
145
+ top_level_key = :data
146
+ if @graphql
147
+ top_level_key = @resource.graphql_entrypoint
148
+ unless serializers.is_a?(Array)
149
+ top_level_key = top_level_key.to_s.singularize.to_sym
150
+ end
151
+ end
152
+
153
+ hash[top_level_key] = if serializers.is_a?(Array)
47
154
  serializers.map do |s|
48
155
  s.to_hash(**opts)
49
156
  end
@@ -96,7 +96,18 @@ module Graphiti
96
96
  sl_resource = resource_for_sideload(sideload)
97
97
  query_parents = parents + [self]
98
98
  sub_hash = sub_hash[:include] if sub_hash.key?(:include)
99
- hash[key] = Query.new(sl_resource, @params, key, sub_hash, query_parents, :all)
99
+
100
+ # NB: To handle on__<type>--<name>
101
+ # A) relationship_name == :positions
102
+ # B) key == on__employees.positions
103
+ # This way A) ensures sideloads are resolved
104
+ # And B) ensures nested filters, sorts etc still work
105
+ relationship_name = sideload ? sideload.name : key
106
+ hash[relationship_name] = Query.new sl_resource,
107
+ @params,
108
+ key,
109
+ sub_hash,
110
+ query_parents, :all
100
111
  else
101
112
  handle_missing_sideload(key)
102
113
  end
@@ -17,8 +17,20 @@ module Graphiti
17
17
  render(self.class.jsonapi_renderer).to_json
18
18
  end
19
19
 
20
+ def as_graphql
21
+ render(self.class.graphql_renderer(@proxy))
22
+ end
23
+
24
+ def to_graphql
25
+ as_graphql.to_json
26
+ end
27
+
20
28
  def to_json
21
- render(self.class.hash_renderer(@proxy)).to_json
29
+ as_json.to_json
30
+ end
31
+
32
+ def as_json
33
+ render(self.class.hash_renderer(@proxy))
22
34
  end
23
35
 
24
36
  def to_xml
@@ -35,6 +47,11 @@ module Graphiti
35
47
  JSONAPI::Serializable::Renderer.new(implementation)
36
48
  end
37
49
 
50
+ def self.graphql_renderer(proxy)
51
+ implementation = Graphiti::HashRenderer.new(proxy.resource, graphql: true)
52
+ JSONAPI::Serializable::Renderer.new(implementation)
53
+ end
54
+
38
55
  private
39
56
 
40
57
  def render(renderer)
@@ -83,7 +83,8 @@ module Graphiti
83
83
  :relationships_readable_by_default,
84
84
  :relationships_writable_by_default,
85
85
  :filters_accept_nil_by_default,
86
- :filters_deny_empty_by_default
86
+ :filters_deny_empty_by_default,
87
+ :graphql_entrypoint
87
88
 
88
89
  class << self
89
90
  prepend Overrides
@@ -97,6 +98,7 @@ module Graphiti
97
98
  # re-assigning causes a new Class.new
98
99
  klass.serializer = (klass.serializer || klass.infer_serializer_superclass)
99
100
  klass.type ||= klass.infer_type
101
+ klass.graphql_entrypoint = klass.type.to_s.pluralize.to_sym
100
102
  default(klass, :attributes_readable_by_default, true)
101
103
  default(klass, :attributes_writable_by_default, true)
102
104
  default(klass, :attributes_sortable_by_default, true)
@@ -144,6 +146,7 @@ module Graphiti
144
146
  if (@abstract_class = val)
145
147
  self.serializer = nil
146
148
  self.type = nil
149
+ self.graphql_entrypoint = nil
147
150
  end
148
151
  end
149
152
 
@@ -46,7 +46,7 @@ module Graphiti
46
46
  private
47
47
 
48
48
  def validate!(params)
49
- return unless validate_endpoints?
49
+ return if Graphiti.context[:graphql] || !validate_endpoints?
50
50
 
51
51
  if context&.respond_to?(:request)
52
52
  path = context.request.env["PATH_INFO"]
@@ -42,9 +42,14 @@ module Graphiti
42
42
  end
43
43
 
44
44
  def sideload(name)
45
- sl = super
45
+ if (split_on = name.to_s.split(/^on__/)).length > 1
46
+ on_type, name = split_on[1].split("--").map(&:to_sym)
47
+ end
48
+
49
+ sl = super(name)
46
50
  if !polymorphic_child? && sl.nil?
47
51
  children.each do |c|
52
+ next if on_type && c.type != on_type
48
53
  break if (sl = c.sideloads[name])
49
54
  end
50
55
  end
@@ -31,7 +31,7 @@ module Graphiti
31
31
  end
32
32
 
33
33
  def before_resolve(scope, query)
34
- scope[:params] = Util::RemoteParams.generate(self, query)
34
+ scope[:params] = Util::RemoteParams.generate(self, query, scope[:foreign_key])
35
35
  scope
36
36
  end
37
37
 
@@ -47,10 +47,22 @@ module Graphiti
47
47
  Renderer.new(self, options).to_json
48
48
  end
49
49
 
50
+ def as_json(options = {})
51
+ Renderer.new(self, options).as_json
52
+ end
53
+
50
54
  def to_xml(options = {})
51
55
  Renderer.new(self, options).to_xml
52
56
  end
53
57
 
58
+ def to_graphql(options = {})
59
+ Renderer.new(self, options).to_graphql
60
+ end
61
+
62
+ def as_graphql(options = {})
63
+ Renderer.new(self, options).as_graphql
64
+ end
65
+
54
66
  def data
55
67
  @data ||= begin
56
68
  records = @scope.resolve
@@ -7,6 +7,7 @@ module Graphiti
7
7
  ::Rails.application.eager_load! if defined?(::Rails)
8
8
  resources ||= Graphiti.resources.reject(&:abstract_class?)
9
9
  resources.reject! { |r| r.name.nil? }
10
+
10
11
  new(resources).generate
11
12
  end
12
13
 
@@ -25,7 +26,7 @@ module Graphiti
25
26
 
26
27
  def initialize(resources)
27
28
  @resources = resources.sort_by(&:name)
28
- @remote_resources = resources.select(&:remote?)
29
+ @remote_resources = @resources.select(&:remote?)
29
30
  @local_resources = @resources - @remote_resources
30
31
  end
31
32
 
@@ -89,6 +90,7 @@ module Graphiti
89
90
  config = {
90
91
  name: r.name,
91
92
  type: r.type.to_s,
93
+ graphql_entrypoint: r.graphql_entrypoint.to_s,
92
94
  description: r.description,
93
95
  attributes: attributes(r),
94
96
  extra_attributes: extra_attributes(r),
@@ -108,7 +110,7 @@ module Graphiti
108
110
  config[:default_page_size] = r.default_page_size
109
111
  end
110
112
 
111
- if r.polymorphic?
113
+ if r.polymorphic? && !r.polymorphic_child?
112
114
  config[:polymorphic] = true
113
115
  config[:children] = r.children.map(&:name)
114
116
  end
@@ -202,6 +204,9 @@ module Graphiti
202
204
  config[:guard] = true
203
205
  end
204
206
  end
207
+ if filter[:required] # one-off filter, not attribute
208
+ config[:required] = true
209
+ end
205
210
  f[name] = config
206
211
  end
207
212
  end
@@ -214,6 +219,7 @@ module Graphiti
214
219
  if config.type == :polymorphic_belongs_to
215
220
  schema[:resources] = config.children.values
216
221
  .map(&:resource).map(&:class).map(&:name)
222
+ schema[:parent_resource] = config.parent_resource.class.name
217
223
  else
218
224
  schema[:resource] = config.resource.class.name
219
225
  end
@@ -12,13 +12,36 @@ module Graphiti
12
12
  # go through type checking/coercion
13
13
  class_attribute :attributes_applied_via_resource
14
14
  class_attribute :extra_attributes_applied_via_resource
15
+ class_attribute :relationship_condition_blocks
15
16
  self.attributes_applied_via_resource = []
16
17
  self.extra_attributes_applied_via_resource = []
18
+ # See #requested_relationships
19
+ self.relationship_condition_blocks ||= {}
17
20
 
18
21
  def self.inherited(klass)
19
22
  super
20
23
  klass.class_eval do
21
24
  extend JSONAPI::Serializable::Resource::ConditionalFields
25
+
26
+ # See #requested_relationships
27
+ def self.relationship(name, options = {}, &block)
28
+ super
29
+ field_condition_blocks.delete(name)
30
+ _register_condition(relationship_condition_blocks, name, options)
31
+ end
32
+
33
+ # NB - avoid clobbering includes when sparse fieldset
34
+ # https://github.com/jsonapi-rb/jsonapi-serializable/pull/102
35
+ #
36
+ # We also override this method to ensure attributes and relationships
37
+ # have separate condition blocks. This way an attribute and
38
+ # relationship can have the same name, and the attribute can be
39
+ # conditional without affecting the relationship.
40
+ def requested_relationships(fields)
41
+ @_relationships.select do |k, _|
42
+ _conditionally_included?(self.class.relationship_condition_blocks, k)
43
+ end
44
+ end
22
45
  end
23
46
  end
24
47
 
@@ -29,12 +52,6 @@ module Graphiti
29
52
  end
30
53
  end
31
54
 
32
- # Temporary fix until fixed upstream
33
- # https://github.com/jsonapi-rb/jsonapi-serializable/pull/102
34
- def requested_relationships(fields)
35
- @_relationships
36
- end
37
-
38
55
  # Allow access to resource methods
39
56
  def method_missing(id, *args, &blk)
40
57
  if @resource.respond_to?(id, true)
@@ -209,7 +209,9 @@ module Graphiti
209
209
  end
210
210
 
211
211
  with_error_handling(Errors::SideloadQueryBuildingError) do
212
- proxy = resource.class._all(params, opts, base_scope)
212
+ scope = base_scope
213
+ scope[:foreign_key] = foreign_key if remote?
214
+ proxy = resource.class._all(params, opts, scope)
213
215
  pre_load_proc&.call(proxy, parents)
214
216
  end
215
217
 
@@ -20,6 +20,12 @@ module Graphiti
20
20
  split = namespace.split("::")
21
21
  split[0, split.length - 1].join("::")
22
22
  end
23
+
24
+ def self.graphql_type_name(name)
25
+ name.gsub("Resource", "")
26
+ .gsub("::", "") # remove modules
27
+ .gsub(".", "__") # remove remote resource .
28
+ end
23
29
  end
24
30
  end
25
31
  end
@@ -2,13 +2,14 @@
2
2
  module Graphiti
3
3
  module Util
4
4
  class RemoteParams
5
- def self.generate(resource, query)
6
- new(resource, query).generate
5
+ def self.generate(resource, query, foreign_key)
6
+ new(resource, query, foreign_key).generate
7
7
  end
8
8
 
9
- def initialize(resource, query)
9
+ def initialize(resource, query, foreign_key)
10
10
  @resource = resource
11
11
  @query = query
12
+ @foreign_key = foreign_key
12
13
  @sorts = []
13
14
  @filters = {}
14
15
  @fields = {}
@@ -97,7 +98,11 @@ module Graphiti
97
98
  return unless fields
98
99
 
99
100
  fields.each_pair do |type, attrs|
100
- @fields[type] = attrs.join(",")
101
+ all_attrs = attrs
102
+ if @foreign_key
103
+ all_attrs |= [@foreign_key]
104
+ end
105
+ @fields[type] = all_attrs.join(",")
101
106
  end
102
107
  end
103
108
 
@@ -44,6 +44,7 @@ module Graphiti
44
44
  model.delete_field(:_relationships)
45
45
  # If this isn't set, Array(resources) will return []
46
46
  # This is important, because jsonapi-serializable makes this call
47
+ # To see the test failure, remote resource position > department
47
48
  model.instance_variable_set(:@__graphiti_resource, 1)
48
49
  model.instance_variable_set(:@__graphiti_serializer, serializer)
49
50
  end
@@ -12,18 +12,20 @@ module Graphiti
12
12
  def apply
13
13
  return unless readable?
14
14
 
15
+ remove_guard if previously_guarded?
16
+
15
17
  if @name == :id
16
18
  @serializer.id(&proc)
17
19
  elsif @attr[:proc]
18
20
  @serializer.send(_method, @name, serializer_options, &proc)
19
21
  elsif @serializer.attribute_blocks[@name].nil?
20
22
  @serializer.send(_method, @name, serializer_options, &proc)
23
+ elsif @serializer.send(applied_method).include?(@name)
24
+ @serializer.field_condition_blocks[@name] = guard if guard?
21
25
  else
22
- unless @serializer.send(applied_method).include?(@name)
23
- inner = @serializer.attribute_blocks.delete(@name)
24
- wrapped = wrap_proc(inner)
25
- @serializer.send(_method, @name, serializer_options, &wrapped)
26
- end
26
+ inner = @serializer.attribute_blocks.delete(@name)
27
+ wrapped = wrap_proc(inner)
28
+ @serializer.send(_method, @name, serializer_options, &wrapped)
27
29
  end
28
30
 
29
31
  existing = @serializer.send(applied_method)
@@ -32,6 +34,14 @@ module Graphiti
32
34
 
33
35
  private
34
36
 
37
+ def previously_guarded?
38
+ @serializer.field_condition_blocks[@name]
39
+ end
40
+
41
+ def remove_guard
42
+ @serializer.field_condition_blocks.delete(@name)
43
+ end
44
+
35
45
  def applied_method
36
46
  if extra?
37
47
  :extra_attributes_applied_via_resource
@@ -56,16 +66,21 @@ module Graphiti
56
66
  method_name = @attr[:readable]
57
67
  instance = @resource.new
58
68
  attribute = @name.to_s
69
+ resource_class = @resource
59
70
 
60
71
  -> {
61
72
  method = instance.method(method_name)
62
- if method.arity.zero?
73
+ result = if method.arity.zero?
63
74
  instance.instance_eval(&method_name)
64
75
  elsif method.arity == 1
65
76
  instance.instance_exec(@object, &method)
66
77
  else
67
78
  instance.instance_exec(@object, attribute, &method)
68
79
  end
80
+ if Graphiti.context[:graphql] && !result
81
+ raise ::Graphiti::Errors::UnreadableAttribute.new(resource_class, attribute)
82
+ end
83
+ result
69
84
  }
70
85
  end
71
86
 
@@ -1,3 +1,3 @@
1
1
  module Graphiti
2
- VERSION = "1.2.31"
2
+ VERSION = "1.2.32"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphiti
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.31
4
+ version: 1.2.32
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-02 00:00:00.000000000 Z
11
+ date: 2021-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jsonapi-serializable