elasticgraph-graphql 0.18.0.5 → 0.19.0.0.rc1

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: 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: []