elasticgraph-graphql 0.18.0.5 → 0.19.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 53e5c759fafb5fe181ae65912c42b21d6d6dbe8cbd3558962cb5a56bc94944b6
4
- data.tar.gz: 862a18dd839b8ee841b91430eb7188612c5483c5701930cf23cfbb22ce44e876
3
+ metadata.gz: 01af70cea7a26c2897ca64c179e0ed5a9a482a9229a46304b2e59b2f249148c3
4
+ data.tar.gz: 9a6aca68a2663012e54bf022a7153ec4443b3bc59846c63b91e6084b07b61574
5
5
  SHA512:
6
- metadata.gz: 4739bc5c2978a75528777b58073ba9e606a5964ff1adcfbb1b19c77eb43e29c5bb19e5aa137d2b530bd2937ca5499538149959b23cf55a7912f9b04b50809e3f
7
- data.tar.gz: fb6b7226810f2f58bd7aedca80a9e1be097ffbbc8dbb5532805f9d98392006e2c4f0db7344c64250c3bd6adcdb917972e230324cc876c0639bba3a40d0601067
6
+ metadata.gz: 5ead565225393723335e8dcc0dea15a6858dd005e5f96531ad60aa21b2cf0166760cb5c25e788b0dbb5d367c50ddb1335f9fd5129a2ee173df0d04c436e5ed15
7
+ data.tar.gz: fc360a136de5620ac53375046dce9e93a90b19f4ea3eb2331f8ca2dbda2f2701b55156e04ac60f71ab4bf45c5eca4e45a2efcf2236cef25f78503ef1beca5cda
@@ -13,7 +13,7 @@ ElasticGraphGemspecHelper.define_elasticgraph_gem(gemspec_file: __FILE__, catego
13
13
 
14
14
  spec.add_dependency "elasticgraph-datastore_core", eg_version
15
15
  spec.add_dependency "elasticgraph-schema_artifacts", eg_version
16
- spec.add_dependency "graphql", "~> 2.3.14"
16
+ spec.add_dependency "graphql", "~> 2.4.5"
17
17
 
18
18
  spec.add_development_dependency "elasticgraph-admin", eg_version
19
19
  spec.add_development_dependency "elasticgraph-elasticsearch", eg_version
@@ -114,8 +114,17 @@ module ElasticGraph
114
114
  # (by doc count descending, then by the key values ascending), and then take only first `size`.
115
115
  def sort_and_truncate_buckets(buckets, size)
116
116
  buckets
117
- .sort_by { |b| [-b.fetch("doc_count"), b.fetch("key_values")] }
118
- .first(size)
117
+ .sort_by do |b|
118
+ # We convert key values to strings to ensure they are comparable. Otherwise, we can get an error like:
119
+ #
120
+ # > ArgumentError: comparison of Array with Array failed
121
+ #
122
+ # Note that this turns it into a lexicographical sort rather than a more type-aware sort
123
+ # (10 will sort before 2, for example), but that's fine. We only sort by `key_values` as
124
+ # a time breaker to ensure deterministic results, but don't particularly care which buckets
125
+ # come first.
126
+ [-b.fetch("doc_count"), b.fetch("key_values").map(&:to_s)]
127
+ end.first(size)
119
128
  end
120
129
 
121
130
  def missing_bucket_path_from(buckets_path)
@@ -15,7 +15,7 @@ module ElasticGraph
15
15
  # Responsible for building a search index expression for a specific query based on the filters.
16
16
  class IndexExpressionBuilder
17
17
  def initialize(schema_names:)
18
- @filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(schema_names, Support::TimeSet::ALL) do |operator, filter_value|
18
+ @filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(schema_names, Support::TimeSet::ALL, Support::TimeSet::EMPTY) do |operator, filter_value|
19
19
  case operator
20
20
  when :gt, :gte, :lt, :lte
21
21
  if date_string?(filter_value)
@@ -16,8 +16,9 @@ module ElasticGraph
16
16
  def initialize(schema_names:)
17
17
  # @type var all_values_set: _RoutingValueSet
18
18
  all_values_set = RoutingValueSet::ALL
19
+ empty_set = RoutingValueSet::EMPTY
19
20
 
20
- @filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(schema_names, all_values_set) do |operator, filter_value|
21
+ @filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(schema_names, all_values_set, empty_set) do |operator, filter_value|
21
22
  if operator == :equal_to_any_of
22
23
  # This calls `.compact` to remove `nil` filter_value values
23
24
  RoutingValueSet.of(filter_value.compact)
@@ -70,6 +71,7 @@ module ElasticGraph
70
71
  end
71
72
 
72
73
  ALL = of_all_except([])
74
+ EMPTY = of([])
73
75
 
74
76
  def intersection(other_set)
75
77
  # Here we return `self` to preserve the commutative property of `intersection`. Returning `self`
@@ -61,7 +61,7 @@ module ElasticGraph
61
61
  # Unfortunately, the Elasticsearch/OpenSearch clients don't support setting a per-request client-side timeout,
62
62
  # even though Faraday (the underlying HTTP client) does. To work around this, we pass our desired
63
63
  # timeout in a specific header that the `SupportTimeouts` Faraday middleware will use.
64
- headers = {TIMEOUT_MS_HEADER => msearch_request_timeout_from(queries)}.compact
64
+ headers = {TIMEOUT_MS_HEADER => msearch_request_timeout_from(queries)&.to_s}.compact
65
65
 
66
66
  queries_and_header_body_tuples_by_datastore_client = header_body_tuples_by_query.group_by do |(query, header_body_tuples)|
67
67
  @datastore_clients_by_name.fetch(query.cluster_name)
@@ -118,6 +118,8 @@ module ElasticGraph
118
118
  return if failures.empty?
119
119
 
120
120
  formatted_failures = failures.map do |(query, response), index|
121
+ raise_execution_exception_for_known_public_error(response)
122
+
121
123
  # Note: we intentionally omit the body of the request here, because it could contain PII
122
124
  # or other sensitive values that we don't want logged.
123
125
  <<~ERROR
@@ -130,6 +132,21 @@ module ElasticGraph
130
132
  raise Errors::SearchFailedError, "Got #{failures.size} search failure(s):\n\n#{formatted_failures}"
131
133
  end
132
134
 
135
+ # In general, when we receive a datastore response indicating a search failed, we raise
136
+ # `Errors::SearchFailedError` which translates into a `500 Internal Server Error`. That's
137
+ # appropriate for transient errors (particularly when there's nothing the client can do about
138
+ # it) but for non-transient errors that the client can do something about, we'd like to provide
139
+ # a friendlier error. This method handles those cases.
140
+ #
141
+ # GraphQL::ExecutionError is automatically translated into a nice error response.
142
+ def raise_execution_exception_for_known_public_error(response)
143
+ if response.dig("error", "caused_by", "type") == "too_many_buckets_exception"
144
+ max_buckets = response.dig("error", "caused_by", "max_buckets")
145
+ raise ::GraphQL::ExecutionError, "Aggregation query produces too many groupings. " \
146
+ "Reduce the grouping cardinality to less than #{max_buckets} and try again."
147
+ end
148
+ end
149
+
133
150
  # Examine successful query responses and log any shard failure they encounter
134
151
  def log_shard_failure_if_necessary(responses_by_query)
135
152
  shard_failures = responses_by_query.each_with_index.select do |(query, response), query_numeric_index|
@@ -33,12 +33,7 @@ module ElasticGraph
33
33
  bool_node[occurrence].concat(clauses)
34
34
  end
35
35
 
36
- # For `any_of: []` we need a way to force the datastore to match no documents, but
37
- # I haven't found any sort of literal `false` we can pass in the compound expression
38
- # or even a literal `1 = 0` as is sometimes used in SQL. Instead, we use this for that
39
- # case.
40
- empty_array = [] # : ::Array[untyped]
41
- ALWAYS_FALSE_FILTER = filter({ids: {values: empty_array}})
36
+ ALWAYS_FALSE_FILTER = filter({match_none: {}})
42
37
  end
43
38
  end
44
39
  end
@@ -59,14 +59,9 @@ module ElasticGraph
59
59
 
60
60
  def process_filter_hash(bool_node, filter_hash, field_path)
61
61
  filter_hash.each do |field_or_op, expression|
62
- # `nil` filter predicates should be ignored, so we can safely `compact` them out.
63
- # It also is simpler to handle them once here instead of the different branches
64
- # below having to be aware of possible `nil` predicates.
65
- expression = expression.compact if expression.is_a?(::Hash)
66
-
67
62
  case filter_node_interpreter.identify_node_type(field_or_op, expression)
68
63
  when :empty
69
- # This is an "empty" filter predicate and we can ignore it.
64
+ # This is an "empty" filter predicate and can be treated as `true`.
70
65
  when :not
71
66
  process_not_expression(bool_node, expression, field_path)
72
67
  when :list_any_filter
@@ -109,10 +104,14 @@ module ElasticGraph
109
104
 
110
105
  def process_not_expression(bool_node, expression, field_path)
111
106
  sub_filter = build_bool_hash do |inner_node|
112
- process_filter_hash(inner_node, expression, field_path)
107
+ process_filter_hash(inner_node, expression || {}, field_path)
113
108
  end
114
109
 
115
- return unless sub_filter
110
+ unless sub_filter
111
+ # Since an empty expression is treated as `true`, convert to `false` when negating.
112
+ BooleanQuery::ALWAYS_FALSE_FILTER.merge_into(bool_node)
113
+ return
114
+ end
116
115
 
117
116
  # Prevent any negated filters from being unnecessarily double-negated by
118
117
  # converting them to a positive filter (i.e., !!A == A).
@@ -138,6 +137,8 @@ module ElasticGraph
138
137
  # this because we do not generate `any_satisfy` filters on `object` list fields (instead,
139
138
  # they get generated on their leaf fields).
140
139
  def process_list_any_filter_expression(bool_node, filter, field_path)
140
+ return if filter.nil? || filter == {}
141
+
141
142
  if filters_on_sub_fields?(filter)
142
143
  process_any_satisfy_filter_expression_on_nested_object_list(bool_node, filter, field_path)
143
144
  else
@@ -188,22 +189,37 @@ module ElasticGraph
188
189
  end
189
190
  end
190
191
 
192
+ # We want to provide the following semantics for `any_of`:
193
+ #
194
+ # * `filter: {anyOf: []}` -> return no results
195
+ # * `filter: {anyOf: [{field: null}]}` -> return all results
196
+ # * `filter: {anyOf: [{field: null}, {field: ...}]}` -> return all results
191
197
  def process_any_of_expression(bool_node, expressions, field_path)
198
+ return if expressions.nil? || expressions == {}
199
+
200
+ if expressions.empty?
201
+ # When our `expressions` array is empty, we want to match no documents. However, that's
202
+ # not the behavior the datastore will give us if we have an empty array in the query under
203
+ # `should`. To get the behavior we want, we need to pass the datastore some filter criteria
204
+ # that will evaluate to false for every document.
205
+ BooleanQuery::ALWAYS_FALSE_FILTER.merge_into(bool_node)
206
+ return
207
+ end
208
+
192
209
  shoulds = expressions.filter_map do |expression|
193
210
  build_bool_hash do |inner_bool_node|
194
211
  process_filter_hash(inner_bool_node, expression, field_path)
195
212
  end
196
213
  end
197
214
 
198
- # When our `shoulds` array is empty, the filtering semantics we want is to match no documents.
199
- # However, that's not the behavior the datastore will give us if we have an empty array in the
200
- # query under `should`. To get the behavior we want, we need to pass the datastore some filter
201
- # criteria that will evaluate to false for every document.
202
- bool_query = shoulds.empty? ? BooleanQuery::ALWAYS_FALSE_FILTER : BooleanQuery.should(*shoulds)
203
- bool_query.merge_into(bool_node)
215
+ return if shoulds.size < expressions.size
216
+
217
+ BooleanQuery.should(*shoulds).merge_into(bool_node)
204
218
  end
205
219
 
206
220
  def process_all_of_expression(bool_node, expressions, field_path)
221
+ return if expressions.nil? || expressions == {}
222
+
207
223
  # `all_of` represents an AND. AND is the default way that `process_filter_hash` combines
208
224
  # filters so we just have to call it for each sub-expression.
209
225
  expressions.each do |sub_expression|
@@ -212,6 +228,8 @@ module ElasticGraph
212
228
  end
213
229
 
214
230
  def process_operator_expression(bool_node, operator, expression, field_path)
231
+ return if expression.nil? || expression == {}
232
+
215
233
  # `operator` is a filtering operator, and `expression` is the value the filtering
216
234
  # operator should be applied to. The `op_applicator` lambda, when called, will
217
235
  # return a Clause instance (defined in this module).
@@ -299,6 +317,8 @@ module ElasticGraph
299
317
  end
300
318
 
301
319
  def process_list_count_expression(bool_node, expression, field_path)
320
+ return if expression.nil? || expression == {}
321
+
302
322
  # Normally, we don't have to do anything special for list count expressions.
303
323
  # That's the case, for example, for an expression like:
304
324
  #
@@ -313,7 +333,7 @@ module ElasticGraph
313
333
  # While we index an explicit count of 0, the count field will be missing from documents indexed before
314
334
  # the list field was defined on the ElasticGraph schema. To properly match those documents, we need to
315
335
  # convert this into an OR (using `any_of`) to also match documents that lack the field entirely.
316
- unless excludes_zero?(expression)
336
+ if filters_to_range_including_zero?(expression)
317
337
  expression = {schema_names.any_of => [
318
338
  expression,
319
339
  {schema_names.equal_to_any_of => [nil]}
@@ -326,7 +346,7 @@ module ElasticGraph
326
346
  def build_bool_hash(&block)
327
347
  bool_node = Hash.new { |h, k| h[k] = [] }.tap(&block)
328
348
 
329
- # To ignore "empty" filter predicates we need to return `nil` here.
349
+ # To treat "empty" filter predicates as `true` we need to return `nil` here.
330
350
  return nil if bool_node.empty?
331
351
 
332
352
  # According to https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html#bool-min-should-match,
@@ -337,20 +357,28 @@ module ElasticGraph
337
357
  {bool: bool_node}
338
358
  end
339
359
 
340
- # Determines if the given filter expression excludes the value `0`.
341
- def excludes_zero?(expression)
342
- expression.any? do |operator, operand|
343
- case operator
344
- when schema_names.equal_to_any_of then !operand.include?(0)
345
- when schema_names.lt then operand <= 0
346
- when schema_names.lte then operand < 0
347
- when schema_names.gt then operand >= 0
348
- when schema_names.gte then operand > 0
349
- else
350
- # :nocov: -- all operators are covered above. But simplecov complains about an implicit `else` branch being uncovered, so here we've defined it to wrap it with `:nocov:`.
351
- false
352
- # :nocov:
353
- end
360
+ # Determines if the given expression filters to a range that includes `0`.
361
+ # If it does not do any filtering (e.g. an empty expression) it will return `false`.
362
+ def filters_to_range_including_zero?(expression)
363
+ expression = expression.compact
364
+
365
+ expression.size > 0 && expression.none? do |operator, operand|
366
+ operator_excludes_zero?(operator, operand)
367
+ end
368
+ end
369
+
370
+ # Determines if the given operator and operand exclude 0 as a matched value.
371
+ def operator_excludes_zero?(operator, operand)
372
+ case operator
373
+ when schema_names.equal_to_any_of then !operand.include?(0)
374
+ when schema_names.lt then operand <= 0
375
+ when schema_names.lte then operand < 0
376
+ when schema_names.gt then operand >= 0
377
+ when schema_names.gte then operand > 0
378
+ else
379
+ # :nocov: -- all operators are covered above. But simplecov complains about an implicit `else` branch being uncovered, so here we've defined it to wrap it with `:nocov:`.
380
+ false
381
+ # :nocov:
354
382
  end
355
383
  end
356
384
 
@@ -25,22 +25,32 @@ module ElasticGraph
25
25
  end
26
26
 
27
27
  def identify_node_type(field_or_op, sub_expression)
28
- return :empty if sub_expression.nil? || sub_expression == {}
28
+ identify_by_field_or_op(field_or_op) || identify_by_sub_expr(sub_expression) || :unknown
29
+ end
30
+
31
+ def filter_operators
32
+ @filter_operators ||= build_filter_operators(runtime_metadata)
33
+ end
34
+
35
+ private
36
+
37
+ def identify_by_field_or_op(field_or_op)
29
38
  return :not if field_or_op == schema_names.not
30
39
  return :list_any_filter if field_or_op == schema_names.any_satisfy
31
40
  return :all_of if field_or_op == schema_names.all_of
32
41
  return :any_of if field_or_op == schema_names.any_of
33
42
  return :operator if filter_operators.key?(field_or_op)
34
43
  return :list_count if field_or_op == LIST_COUNTS_FIELD
35
- return :sub_field if sub_expression.is_a?(::Hash)
36
- :unknown
37
- end
38
44
 
39
- def filter_operators
40
- @filter_operators ||= build_filter_operators(runtime_metadata)
45
+ nil
41
46
  end
42
47
 
43
- private
48
+ def identify_by_sub_expr(sub_expression)
49
+ return :empty if sub_expression.nil? || sub_expression == {}
50
+ return :sub_field if sub_expression.is_a?(::Hash)
51
+
52
+ nil
53
+ end
44
54
 
45
55
  def build_filter_operators(runtime_metadata)
46
56
  filter_by_time_of_day_script_id = runtime_metadata
@@ -12,9 +12,10 @@ module ElasticGraph
12
12
  # Responsible for extracting a set of values from query filters, based on a using a custom
13
13
  # set type that is able to efficiently model the "all values" case.
14
14
  class FilterValueSetExtractor
15
- def initialize(schema_names, all_values_set, &build_set_for_filter)
15
+ def initialize(schema_names, all_values_set, empty_set, &build_set_for_filter)
16
16
  @schema_names = schema_names
17
17
  @all_values_set = all_values_set
18
+ @empty_set = empty_set
18
19
  @build_set_for_filter = build_set_for_filter
19
20
  end
20
21
 
@@ -55,7 +56,7 @@ module ElasticGraph
55
56
  # outside the `map_reduce_sets` block below so we only do it once instead of N times.
56
57
  target_field_path_parts = target_field_path.split(".")
57
58
 
58
- # Here we intersect the filter value setbecause when we have multiple `filter_hashes`,
59
+ # Here we intersect the filter value set, because when we have multiple `filter_hashes`,
59
60
  # the filters are ANDed together. Only documents that match ALL the filters will be
60
61
  # returned. Therefore, we want the intersection of filter value sets.
61
62
  map_reduce_sets(filter_hashes, :intersection, negate: false) do |filter_hash|
@@ -83,7 +84,7 @@ module ElasticGraph
83
84
  # to a particular field.
84
85
  def filter_value_set_for_filter_hash_entry(field_or_op, filter_value, target_field_path_parts, traversed_field_path_parts, negate:)
85
86
  if filter_value.nil?
86
- # Any filter with a `nil` value is effectively ignored by our filtering logic, so we need
87
+ # Any filter with a `nil` value is effectively treated as `true` by our filtering logic, so we need
87
88
  # to return our `@all_values_set` to indicate this filter matches all documents.
88
89
  @all_values_set
89
90
  elsif field_or_op == @schema_names.not
@@ -105,6 +106,11 @@ module ElasticGraph
105
106
 
106
107
  # Determines the set of filter values for an `any_of` clause, which is used for ORing multiple filters together.
107
108
  def filter_value_set_for_any_of(filter_hashes, target_field_path_parts, traversed_field_path_parts, negate:)
109
+ # Here we treat `any_of: []` as matching no values.
110
+ if filter_hashes.empty?
111
+ return negate ? @all_values_set : @empty_set
112
+ end
113
+
108
114
  # Here we union the filter value sets because `any_of` represents an OR. If we can determine specific
109
115
  # filter values for all `any_of` clauses, we will OR them together. Alternately, if we cannot
110
116
  # determine specific filter values for any clauses, we will union `@all_values_set`,
@@ -130,6 +130,10 @@ module ElasticGraph
130
130
  # Ignore an empty string operationName.
131
131
  params = params.merge("operationName" => nil) if params["operationName"] && params["operationName"].empty?
132
132
 
133
+ if (variables = params["variables"]) && !variables.is_a?(::Hash)
134
+ return HTTPResponse.error(400, "`variables` must be a JSON object but was not.")
135
+ end
136
+
133
137
  yield params
134
138
  end
135
139
 
@@ -101,7 +101,7 @@ module ElasticGraph
101
101
 
102
102
  def filter_value_set_extractor
103
103
  @filter_value_set_extractor ||=
104
- Filtering::FilterValueSetExtractor.new(schema_element_names, IncludesNilSet) do |operator, filter_value|
104
+ Filtering::FilterValueSetExtractor.new(schema_element_names, IncludesNilSet, ExcludesNilSet) do |operator, filter_value|
105
105
  if operator == :equal_to_any_of && filter_value.include?(nil)
106
106
  IncludesNilSet
107
107
  else
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # frozen_string_literal: true
8
8
 
9
- require "graphql/dataloader/source"
9
+ require "graphql"
10
10
 
11
11
  module ElasticGraph
12
12
  class GraphQL
@@ -23,7 +23,6 @@ module ElasticGraph
23
23
  Support::MemoizableData.define(:schema_element_names, *fields) do
24
24
  # @implements ResolvableValueClass
25
25
  include ResolvableValue
26
- # @type var block: (^() -> void)?
27
26
  class_exec(&block) if block
28
27
  end
29
28
  end
@@ -29,7 +29,7 @@ module ElasticGraph
29
29
  scalar_types.to_set.union(introspection_types)
30
30
  )
31
31
 
32
- attr_reader :element_names, :defined_types, :config, :graphql_schema, :runtime_metadata
32
+ attr_reader :element_names, :config, :graphql_schema, :runtime_metadata
33
33
 
34
34
  def initialize(
35
35
  graphql_schema_string:,
@@ -53,7 +53,6 @@ module ElasticGraph
53
53
  )
54
54
  end
55
55
 
56
- @types_by_name = Hash.new { |hash, key| hash[key] = lookup_type_by_name(key) }
57
56
  @build_resolver = build_resolver
58
57
 
59
58
  # Note: as part of loading the schema, the GraphQL gem may use the resolver (such
@@ -68,7 +67,7 @@ module ElasticGraph
68
67
 
69
68
  # Pre-load all defined types so that all field extras can get configured as part
70
69
  # of loading the schema, before we execute the first query.
71
- @defined_types = build_defined_types_array(@graphql_schema)
70
+ @types_by_name = build_types_by_name
72
71
  end
73
72
 
74
73
  def type_from(graphql_type)
@@ -80,7 +79,11 @@ module ElasticGraph
80
79
  # get type objects for wrapped types, but you need to get it from a field object of that
81
80
  # type.
82
81
  def type_named(type_name)
83
- @types_by_name[type_name.to_s]
82
+ @types_by_name.fetch(type_name.to_s)
83
+ rescue KeyError => e
84
+ msg = "No type named #{type_name} could be found"
85
+ msg += "; Possible alternatives: [#{e.corrections.join(", ").delete('"')}]." if e.corrections.any?
86
+ raise Errors::NotFoundError, msg
84
87
  end
85
88
 
86
89
  def document_type_stored_in(index_definition_name)
@@ -106,6 +109,10 @@ module ElasticGraph
106
109
  @indexed_document_types ||= defined_types.select(&:indexed_document?)
107
110
  end
108
111
 
112
+ def defined_types
113
+ @defined_types ||= @types_by_name.except(*BUILT_IN_TYPE_NAMES).values
114
+ end
115
+
109
116
  def to_s
110
117
  "#<#{self.class.name} 0x#{__id__.to_s(16)} indexed_document_types=#{indexed_document_types.map(&:name).sort.to_s.delete(":")}>"
111
118
  end
@@ -128,24 +135,14 @@ module ElasticGraph
128
135
  def_delegators :resolver, :call, :resolve_type, :coerce_input, :coerce_result
129
136
  end
130
137
 
131
- def lookup_type_by_name(type_name)
132
- type_from(@graphql_schema.types.fetch(type_name))
133
- rescue KeyError => e
134
- msg = "No type named #{type_name} could be found"
135
- msg += "; Possible alternatives: [#{e.corrections.join(", ").delete('"')}]." if e.corrections.any?
136
- raise Errors::NotFoundError, msg
137
- end
138
-
139
138
  def resolver
140
139
  @resolver ||= @build_resolver.call(self)
141
140
  end
142
141
 
143
- def build_defined_types_array(graphql_schema)
144
- graphql_schema
145
- .types
146
- .values
147
- .reject { |t| BUILT_IN_TYPE_NAMES.include?(t.graphql_name) }
148
- .map { |t| type_named(t.graphql_name) }
142
+ def build_types_by_name
143
+ graphql_schema.types.transform_values do |graphql_type|
144
+ @types_by_graphql_type[graphql_type]
145
+ end
149
146
  end
150
147
 
151
148
  def indexed_document_types_by_index_definition_name
@@ -142,7 +142,13 @@ module ElasticGraph
142
142
  def graphql_gem_plugins
143
143
  @graphql_gem_plugins ||= begin
144
144
  require "graphql"
145
- {::GraphQL::Dataloader => {}}
145
+ {
146
+ # We depend on this to avoid N+1 calls to the datastore.
147
+ ::GraphQL::Dataloader => {},
148
+ # This is new in the graphql-ruby 2.4 release, and will be required in the future.
149
+ # We pass `preload: true` because the way we handle the schema depends on it being preloaded.
150
+ ::GraphQL::Schema::Visibility => {preload: true}
151
+ }
146
152
  end
147
153
  end
148
154
 
@@ -244,6 +250,9 @@ module ElasticGraph
244
250
  # at boot time instead of deferring dependency loading until we handle the first query. In other environments (such as tests),
245
251
  # it's nice to load dependencies when needed.
246
252
  def load_dependencies_eagerly
253
+ require "graphql"
254
+ ::GraphQL.eager_load!
255
+
247
256
  # run a simple GraphQL query to force load any dependencies needed to handle GraphQL queries
248
257
  graphql_query_executor.execute(EAGER_LOAD_QUERY, client: Client::ELASTICGRAPH_INTERNAL)
249
258
  graphql_http_endpoint # force load this too.
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elasticgraph-graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.0.5
4
+ version: 0.19.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Myron Marston
8
8
  - Ben VandenBos
9
- - Square Engineering
9
+ - Block Engineering
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2024-09-20 00:00:00.000000000 Z
13
+ date: 2024-12-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop-factory_bot
@@ -46,42 +46,42 @@ dependencies:
46
46
  requirements:
47
47
  - - "~>"
48
48
  - !ruby/object:Gem::Version
49
- version: '3.0'
49
+ version: '3.1'
50
50
  type: :development
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
- version: '3.0'
56
+ version: '3.1'
57
57
  - !ruby/object:Gem::Dependency
58
58
  name: standard
59
59
  requirement: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: 1.40.0
63
+ version: 1.41.0
64
64
  type: :development
65
65
  prerelease: false
66
66
  version_requirements: !ruby/object:Gem::Requirement
67
67
  requirements:
68
68
  - - "~>"
69
69
  - !ruby/object:Gem::Version
70
- version: 1.40.0
70
+ version: 1.41.0
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: steep
73
73
  requirement: !ruby/object:Gem::Requirement
74
74
  requirements:
75
75
  - - "~>"
76
76
  - !ruby/object:Gem::Version
77
- version: '1.7'
77
+ version: '1.8'
78
78
  type: :development
79
79
  prerelease: false
80
80
  version_requirements: !ruby/object:Gem::Requirement
81
81
  requirements:
82
82
  - - "~>"
83
83
  - !ruby/object:Gem::Version
84
- version: '1.7'
84
+ version: '1.8'
85
85
  - !ruby/object:Gem::Dependency
86
86
  name: coderay
87
87
  requirement: !ruby/object:Gem::Requirement
@@ -136,14 +136,14 @@ dependencies:
136
136
  requirements:
137
137
  - - "~>"
138
138
  - !ruby/object:Gem::Version
139
- version: '0.12'
139
+ version: '0.13'
140
140
  type: :development
141
141
  prerelease: false
142
142
  version_requirements: !ruby/object:Gem::Requirement
143
143
  requirements:
144
144
  - - "~>"
145
145
  - !ruby/object:Gem::Version
146
- version: '0.12'
146
+ version: '0.13'
147
147
  - !ruby/object:Gem::Dependency
148
148
  name: simplecov
149
149
  requirement: !ruby/object:Gem::Requirement
@@ -254,126 +254,126 @@ dependencies:
254
254
  requirements:
255
255
  - - "~>"
256
256
  - !ruby/object:Gem::Version
257
- version: '3.4'
257
+ version: '3.5'
258
258
  type: :development
259
259
  prerelease: false
260
260
  version_requirements: !ruby/object:Gem::Requirement
261
261
  requirements:
262
262
  - - "~>"
263
263
  - !ruby/object:Gem::Version
264
- version: '3.4'
264
+ version: '3.5'
265
265
  - !ruby/object:Gem::Dependency
266
266
  name: elasticgraph-datastore_core
267
267
  requirement: !ruby/object:Gem::Requirement
268
268
  requirements:
269
269
  - - '='
270
270
  - !ruby/object:Gem::Version
271
- version: 0.18.0.5
271
+ version: 0.19.0.0.rc1
272
272
  type: :runtime
273
273
  prerelease: false
274
274
  version_requirements: !ruby/object:Gem::Requirement
275
275
  requirements:
276
276
  - - '='
277
277
  - !ruby/object:Gem::Version
278
- version: 0.18.0.5
278
+ version: 0.19.0.0.rc1
279
279
  - !ruby/object:Gem::Dependency
280
280
  name: elasticgraph-schema_artifacts
281
281
  requirement: !ruby/object:Gem::Requirement
282
282
  requirements:
283
283
  - - '='
284
284
  - !ruby/object:Gem::Version
285
- version: 0.18.0.5
285
+ version: 0.19.0.0.rc1
286
286
  type: :runtime
287
287
  prerelease: false
288
288
  version_requirements: !ruby/object:Gem::Requirement
289
289
  requirements:
290
290
  - - '='
291
291
  - !ruby/object:Gem::Version
292
- version: 0.18.0.5
292
+ version: 0.19.0.0.rc1
293
293
  - !ruby/object:Gem::Dependency
294
294
  name: graphql
295
295
  requirement: !ruby/object:Gem::Requirement
296
296
  requirements:
297
297
  - - "~>"
298
298
  - !ruby/object:Gem::Version
299
- version: 2.3.14
299
+ version: 2.4.5
300
300
  type: :runtime
301
301
  prerelease: false
302
302
  version_requirements: !ruby/object:Gem::Requirement
303
303
  requirements:
304
304
  - - "~>"
305
305
  - !ruby/object:Gem::Version
306
- version: 2.3.14
306
+ version: 2.4.5
307
307
  - !ruby/object:Gem::Dependency
308
308
  name: elasticgraph-admin
309
309
  requirement: !ruby/object:Gem::Requirement
310
310
  requirements:
311
311
  - - '='
312
312
  - !ruby/object:Gem::Version
313
- version: 0.18.0.5
313
+ version: 0.19.0.0.rc1
314
314
  type: :development
315
315
  prerelease: false
316
316
  version_requirements: !ruby/object:Gem::Requirement
317
317
  requirements:
318
318
  - - '='
319
319
  - !ruby/object:Gem::Version
320
- version: 0.18.0.5
320
+ version: 0.19.0.0.rc1
321
321
  - !ruby/object:Gem::Dependency
322
322
  name: elasticgraph-elasticsearch
323
323
  requirement: !ruby/object:Gem::Requirement
324
324
  requirements:
325
325
  - - '='
326
326
  - !ruby/object:Gem::Version
327
- version: 0.18.0.5
327
+ version: 0.19.0.0.rc1
328
328
  type: :development
329
329
  prerelease: false
330
330
  version_requirements: !ruby/object:Gem::Requirement
331
331
  requirements:
332
332
  - - '='
333
333
  - !ruby/object:Gem::Version
334
- version: 0.18.0.5
334
+ version: 0.19.0.0.rc1
335
335
  - !ruby/object:Gem::Dependency
336
336
  name: elasticgraph-opensearch
337
337
  requirement: !ruby/object:Gem::Requirement
338
338
  requirements:
339
339
  - - '='
340
340
  - !ruby/object:Gem::Version
341
- version: 0.18.0.5
341
+ version: 0.19.0.0.rc1
342
342
  type: :development
343
343
  prerelease: false
344
344
  version_requirements: !ruby/object:Gem::Requirement
345
345
  requirements:
346
346
  - - '='
347
347
  - !ruby/object:Gem::Version
348
- version: 0.18.0.5
348
+ version: 0.19.0.0.rc1
349
349
  - !ruby/object:Gem::Dependency
350
350
  name: elasticgraph-indexer
351
351
  requirement: !ruby/object:Gem::Requirement
352
352
  requirements:
353
353
  - - '='
354
354
  - !ruby/object:Gem::Version
355
- version: 0.18.0.5
355
+ version: 0.19.0.0.rc1
356
356
  type: :development
357
357
  prerelease: false
358
358
  version_requirements: !ruby/object:Gem::Requirement
359
359
  requirements:
360
360
  - - '='
361
361
  - !ruby/object:Gem::Version
362
- version: 0.18.0.5
362
+ version: 0.19.0.0.rc1
363
363
  - !ruby/object:Gem::Dependency
364
364
  name: elasticgraph-schema_definition
365
365
  requirement: !ruby/object:Gem::Requirement
366
366
  requirements:
367
367
  - - '='
368
368
  - !ruby/object:Gem::Version
369
- version: 0.18.0.5
369
+ version: 0.19.0.0.rc1
370
370
  type: :development
371
371
  prerelease: false
372
372
  version_requirements: !ruby/object:Gem::Requirement
373
373
  requirements:
374
374
  - - '='
375
375
  - !ruby/object:Gem::Version
376
- version: 0.18.0.5
376
+ version: 0.19.0.0.rc1
377
377
  description:
378
378
  email:
379
379
  - myron@squareup.com
@@ -461,10 +461,15 @@ files:
461
461
  - lib/elastic_graph/graphql/schema/type.rb
462
462
  - script/dump_time_zones
463
463
  - script/dump_time_zones.java
464
- homepage:
464
+ homepage: https://block.github.io/elasticgraph/
465
465
  licenses:
466
466
  - MIT
467
467
  metadata:
468
+ bug_tracker_uri: https://github.com/block/elasticgraph/issues
469
+ changelog_uri: https://github.com/block/elasticgraph/releases/tag/v0.19.0.0.rc1
470
+ documentation_uri: https://block.github.io/elasticgraph/docs/main/
471
+ homepage_uri: https://block.github.io/elasticgraph/
472
+ source_code_uri: https://github.com/block/elasticgraph/tree/v0.19.0.0.rc1/elasticgraph-graphql
468
473
  gem_category: core
469
474
  post_install_message:
470
475
  rdoc_options: []