elasticgraph-graphql 0.18.0.4 → 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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/elasticgraph-graphql.gemspec +1 -1
  3. data/lib/elastic_graph/graphql/aggregation/field_path_encoder.rb +2 -2
  4. data/lib/elastic_graph/graphql/aggregation/key.rb +2 -2
  5. data/lib/elastic_graph/graphql/aggregation/non_composite_grouping_adapter.rb +11 -2
  6. data/lib/elastic_graph/graphql/config.rb +3 -3
  7. data/lib/elastic_graph/graphql/datastore_query/document_paginator.rb +1 -1
  8. data/lib/elastic_graph/graphql/datastore_query/index_expression_builder.rb +1 -1
  9. data/lib/elastic_graph/graphql/datastore_query/paginator.rb +1 -1
  10. data/lib/elastic_graph/graphql/datastore_query/routing_picker.rb +3 -1
  11. data/lib/elastic_graph/graphql/datastore_query.rb +6 -6
  12. data/lib/elastic_graph/graphql/datastore_response/search_response.rb +2 -2
  13. data/lib/elastic_graph/graphql/datastore_search_router.rb +21 -4
  14. data/lib/elastic_graph/graphql/decoded_cursor.rb +7 -7
  15. data/lib/elastic_graph/graphql/filtering/boolean_query.rb +1 -6
  16. data/lib/elastic_graph/graphql/filtering/filter_interpreter.rb +58 -30
  17. data/lib/elastic_graph/graphql/filtering/filter_node_interpreter.rb +17 -7
  18. data/lib/elastic_graph/graphql/filtering/filter_value_set_extractor.rb +9 -3
  19. data/lib/elastic_graph/graphql/http_endpoint.rb +5 -1
  20. data/lib/elastic_graph/graphql/query_adapter/filters.rb +1 -1
  21. data/lib/elastic_graph/graphql/query_executor.rb +1 -1
  22. data/lib/elastic_graph/graphql/resolvers/query_source.rb +1 -1
  23. data/lib/elastic_graph/graphql/resolvers/resolvable_value.rb +2 -3
  24. data/lib/elastic_graph/graphql/schema/arguments.rb +1 -1
  25. data/lib/elastic_graph/graphql/schema/enum_value.rb +2 -2
  26. data/lib/elastic_graph/graphql/schema/field.rb +2 -2
  27. data/lib/elastic_graph/graphql/schema/type.rb +3 -3
  28. data/lib/elastic_graph/graphql/schema.rb +18 -21
  29. data/lib/elastic_graph/graphql.rb +10 -1
  30. metadata +36 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b0cb73d5ec2940bb792cbcada0c615dd1415de774bb9111ffe09ae293cf2de38
4
- data.tar.gz: fb0f23546ee15fd3c9f434518dde7ccb79df0838795392d6b0a8c9e60df01667
3
+ metadata.gz: 01af70cea7a26c2897ca64c179e0ed5a9a482a9229a46304b2e59b2f249148c3
4
+ data.tar.gz: 9a6aca68a2663012e54bf022a7153ec4443b3bc59846c63b91e6084b07b61574
5
5
  SHA512:
6
- metadata.gz: 4c21c60909ba34c7034b2c5506288f54d4e29aa715e9741e1f88a046b54d6a694082b657c00aa495da7d28eb771bec28c2a23d0c10b3c5dbfa46e20088c9802f
7
- data.tar.gz: ecbd8f84362640638e36384875d43c8a9443a413bb407dd2fca5e1d18792bfe618ac5b15c14a9d0c7d2656b7234620d73ce95078bc18ed4cc72eecbec4958ad3
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
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # frozen_string_literal: true
8
8
 
9
- require "elastic_graph/error"
9
+ require "elastic_graph/errors"
10
10
 
11
11
  module ElasticGraph
12
12
  class GraphQL
@@ -38,7 +38,7 @@ module ElasticGraph
38
38
 
39
39
  private_class_method def self.verify_delimiters(str)
40
40
  if str.to_s.include?(DELIMITER)
41
- raise InvalidArgumentValueError, %("#{str}" contains delimiter: "#{DELIMITER}")
41
+ raise Errors::InvalidArgumentValueError, %("#{str}" contains delimiter: "#{DELIMITER}")
42
42
  end
43
43
  end
44
44
  end
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # frozen_string_literal: true
8
8
 
9
- require "elastic_graph/error"
9
+ require "elastic_graph/errors"
10
10
  require "elastic_graph/graphql/aggregation/field_path_encoder"
11
11
 
12
12
  module ElasticGraph
@@ -77,7 +77,7 @@ module ElasticGraph
77
77
  def self.verify_no_delimiter_in(*parts)
78
78
  parts.each do |part|
79
79
  if part.to_s.include?(DELIMITER)
80
- raise InvalidArgumentValueError, %("#{part}" contains delimiter: "#{DELIMITER}")
80
+ raise Errors::InvalidArgumentValueError, %("#{part}" contains delimiter: "#{DELIMITER}")
81
81
  end
82
82
  end
83
83
  end
@@ -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)
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # frozen_string_literal: true
8
8
 
9
- require "elastic_graph/error"
9
+ require "elastic_graph/errors"
10
10
  require "elastic_graph/graphql/client"
11
11
  require "elastic_graph/schema_artifacts/runtime_metadata/extension_loader"
12
12
 
@@ -33,14 +33,14 @@ module ElasticGraph
33
33
  extra_keys = parsed_yaml.keys - EXPECTED_KEYS
34
34
 
35
35
  unless extra_keys.empty?
36
- raise ConfigError, "Unknown `graphql` config settings: #{extra_keys.join(", ")}"
36
+ raise Errors::ConfigError, "Unknown `graphql` config settings: #{extra_keys.join(", ")}"
37
37
  end
38
38
 
39
39
  extension_loader = SchemaArtifacts::RuntimeMetadata::ExtensionLoader.new(::Module.new)
40
40
  extension_mods = parsed_yaml.fetch("extension_modules", []).map do |mod_hash|
41
41
  extension_loader.load(mod_hash.fetch("extension_name"), from: mod_hash.fetch("require_path"), config: {}).extension_class.tap do |mod|
42
42
  unless mod.instance_of?(::Module)
43
- raise ConfigError, "`#{mod_hash.fetch("extension_name")}` is not a module, but all application extension modules must be modules."
43
+ raise Errors::ConfigError, "`#{mod_hash.fetch("extension_name")}` is not a module, but all application extension modules must be modules."
44
44
  end
45
45
  end
46
46
  end
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # frozen_string_literal: true
8
8
 
9
- require "elastic_graph/error"
9
+ require "elastic_graph/errors"
10
10
  require "graphql"
11
11
 
12
12
  module ElasticGraph
@@ -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)
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # frozen_string_literal: true
8
8
 
9
- require "elastic_graph/error"
9
+ require "elastic_graph/errors"
10
10
  require "elastic_graph/support/memoizable_data"
11
11
 
12
12
  module ElasticGraph
@@ -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`
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # frozen_string_literal: true
8
8
 
9
- require "elastic_graph/error"
9
+ require "elastic_graph/errors"
10
10
  require "elastic_graph/graphql/aggregation/query"
11
11
  require "elastic_graph/graphql/aggregation/query_optimizer"
12
12
  require "elastic_graph/graphql/decoded_cursor"
@@ -65,7 +65,7 @@ module ElasticGraph
65
65
  )
66
66
 
67
67
  if search_index_definitions.empty?
68
- raise SearchFailedError, "Query is invalid, since it contains no `search_index_definitions`."
68
+ raise Errors::SearchFailedError, "Query is invalid, since it contains no `search_index_definitions`."
69
69
  end
70
70
  end
71
71
  }
@@ -121,7 +121,7 @@ module ElasticGraph
121
121
  missing_queries = expected_queries - actual_queries
122
122
  extra_queries = actual_queries - expected_queries
123
123
 
124
- raise SearchFailedError, "The `responses_hash` does not have the expected set of queries as keys. " \
124
+ raise Errors::SearchFailedError, "The `responses_hash` does not have the expected set of queries as keys. " \
125
125
  "This can cause problems for the `GraphQL::Dataloader` and suggests a bug in the logic that should be fixed.\n\n" \
126
126
  "Missing queries (#{missing_queries.size}):\n#{missing_queries.map(&:inspect).join("\n")}.\n\n" \
127
127
  "Extra queries (#{extra_queries.size}): #{extra_queries.map(&:inspect).join("\n")}"
@@ -133,7 +133,7 @@ module ElasticGraph
133
133
  # Both query objects are left unchanged.
134
134
  def merge(other_query)
135
135
  if search_index_definitions != other_query.search_index_definitions
136
- raise ElasticGraph::InvalidMergeError, "`search_index_definitions` conflict while merging between " \
136
+ raise ElasticGraph::Errors::InvalidMergeError, "`search_index_definitions` conflict while merging between " \
137
137
  "#{search_index_definitions} and #{other_query.search_index_definitions}"
138
138
  end
139
139
 
@@ -177,11 +177,11 @@ module ElasticGraph
177
177
  end
178
178
 
179
179
  # Returns the name of the datastore cluster as a String where this query should be setn.
180
- # Unless exactly 1 cluster name is found, this method raises a ConfigError.
180
+ # Unless exactly 1 cluster name is found, this method raises a Errors::ConfigError.
181
181
  def cluster_name
182
182
  cluster_name = search_index_definitions.map(&:cluster_to_query).uniq
183
183
  return cluster_name.first if cluster_name.size == 1
184
- raise ConfigError, "Found different datastore clusters (#{cluster_name}) to query " \
184
+ raise Errors::ConfigError, "Found different datastore clusters (#{cluster_name}) to query " \
185
185
  "for query targeting indices: #{search_index_definitions}"
186
186
  end
187
187
 
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # frozen_string_literal: true
8
8
 
9
- require "elastic_graph/error"
9
+ require "elastic_graph/errors"
10
10
  require "elastic_graph/graphql/decoded_cursor"
11
11
  require "elastic_graph/graphql/datastore_response/document"
12
12
  require "forwardable"
@@ -66,7 +66,7 @@ module ElasticGraph
66
66
  end
67
67
 
68
68
  def total_document_count
69
- super || raise(CountUnavailableError, "#{__method__} is unavailable; set `query.total_document_count_needed = true` to make it available")
69
+ super || raise(Errors::CountUnavailableError, "#{__method__} is unavailable; set `query.total_document_count_needed = true` to make it available")
70
70
  end
71
71
 
72
72
  def to_s
@@ -7,7 +7,7 @@
7
7
  # frozen_string_literal: true
8
8
 
9
9
  require "elastic_graph/constants"
10
- require "elastic_graph/error"
10
+ require "elastic_graph/errors"
11
11
  require "elastic_graph/graphql/datastore_response/search_response"
12
12
  require "elastic_graph/graphql/query_details_tracker"
13
13
  require "elastic_graph/support/threading"
@@ -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)
@@ -108,7 +108,7 @@ module ElasticGraph
108
108
 
109
109
  (min_query_deadline - @monotonic_clock.now_in_ms).tap do |timeout|
110
110
  if timeout <= 0
111
- raise RequestExceededDeadlineError, "It is already #{timeout.abs} ms past the search deadline."
111
+ raise Errors::RequestExceededDeadlineError, "It is already #{timeout.abs} ms past the search deadline."
112
112
  end
113
113
  end
114
114
  end
@@ -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
@@ -127,7 +129,22 @@ module ElasticGraph
127
129
  ERROR
128
130
  end.join("\n\n")
129
131
 
130
- raise SearchFailedError, "Got #{failures.size} search failure(s):\n\n#{formatted_failures}"
132
+ raise Errors::SearchFailedError, "Got #{failures.size} search failure(s):\n\n#{formatted_failures}"
133
+ end
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
131
148
  end
132
149
 
133
150
  # Examine successful query responses and log any shard failure they encounter
@@ -8,7 +8,7 @@
8
8
 
9
9
  require "base64"
10
10
  require "elastic_graph/constants"
11
- require "elastic_graph/error"
11
+ require "elastic_graph/errors"
12
12
  require "elastic_graph/support/memoizable_data"
13
13
  require "json"
14
14
 
@@ -39,17 +39,17 @@ module ElasticGraph
39
39
  # Tries to decode the given string cursor, returning `nil` if it is invalid.
40
40
  def self.try_decode(string)
41
41
  decode!(string)
42
- rescue InvalidCursorError
42
+ rescue Errors::InvalidCursorError
43
43
  nil
44
44
  end
45
45
 
46
- # Tries to decode the given string cursor, raising an `InvalidCursorError` if it's invalid.
46
+ # Tries to decode the given string cursor, raising an `Errors::InvalidCursorError` if it's invalid.
47
47
  def self.decode!(string)
48
48
  return SINGLETON if string == SINGLETON_CURSOR
49
49
  json = ::Base64.urlsafe_decode64(string)
50
50
  new(::JSON.parse(json))
51
51
  rescue ::ArgumentError, ::JSON::ParserError
52
- raise InvalidCursorError, "`#{string}` is an invalid cursor."
52
+ raise Errors::InvalidCursorError, "`#{string}` is an invalid cursor."
53
53
  end
54
54
 
55
55
  # Encodes the cursor to a string using JSON and Base64 encoding.
@@ -79,7 +79,7 @@ module ElasticGraph
79
79
  def self.from_sort_list(sort_list)
80
80
  sort_fields = sort_list.map do |hash|
81
81
  if hash.values.any? { |v| !v.is_a?(::Hash) } || hash.values.flat_map(&:keys) != ["order"]
82
- raise InvalidSortFieldsError,
82
+ raise Errors::InvalidSortFieldsError,
83
83
  "Given `sort_list` contained an invalid entry. Each must be a flat hash with one entry. Got: #{sort_list.inspect}"
84
84
  end
85
85
 
@@ -89,7 +89,7 @@ module ElasticGraph
89
89
  end
90
90
 
91
91
  if sort_fields.uniq.size < sort_fields.size
92
- raise InvalidSortFieldsError,
92
+ raise Errors::InvalidSortFieldsError,
93
93
  "Given `sort_list` contains a duplicate field, which the CursorEncoder cannot handler. " \
94
94
  "The caller is responsible for de-duplicating the sort list fist. Got: #{sort_list.inspect}"
95
95
  end
@@ -99,7 +99,7 @@ module ElasticGraph
99
99
 
100
100
  def build(sort_values)
101
101
  unless sort_values.size == sort_fields.size
102
- raise CursorEncodingError,
102
+ raise Errors::CursorEncodingError,
103
103
  "size of sort values (#{sort_values.inspect}) does not match the " \
104
104
  "size of sort fields (#{sort_fields.inspect})"
105
105
  end
@@ -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`,
@@ -62,7 +62,7 @@ module ElasticGraph
62
62
 
63
63
  HTTPResponse.json(200, result.to_h)
64
64
  end
65
- rescue RequestExceededDeadlineError
65
+ rescue Errors::RequestExceededDeadlineError
66
66
  HTTPResponse.error(504, "Search exceeded requested timeout.")
67
67
  end
68
68
 
@@ -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
@@ -29,7 +29,7 @@ module ElasticGraph
29
29
  # Executes the given `query_string` using the provided `variables`.
30
30
  #
31
31
  # `timeout_in_ms` can be provided to limit how long the query runs for. If the timeout
32
- # is exceeded, `RequestExceededDeadlineError` will be raised. Note that `timeout_in_ms`
32
+ # is exceeded, `Errors::RequestExceededDeadlineError` will be raised. Note that `timeout_in_ms`
33
33
  # does not provide an absolute guarantee that the query will take no longer than the
34
34
  # provided value; it is only used to halt datastore queries. In process computation
35
35
  # can make the total query time exceeded the specified timeout.
@@ -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
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # frozen_string_literal: true
8
8
 
9
- require "elastic_graph/error"
9
+ require "elastic_graph/errors"
10
10
  require "elastic_graph/support/memoizable_data"
11
11
 
12
12
  module ElasticGraph
@@ -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
@@ -48,7 +47,7 @@ module ElasticGraph
48
47
 
49
48
  def canonical_name_for(name, element_type)
50
49
  schema_element_names.canonical_name_for(name) ||
51
- raise(SchemaError, "#{element_type} `#{name}` is not a defined schema element")
50
+ raise(Errors::SchemaError, "#{element_type} `#{name}` is not a defined schema element")
52
51
  end
53
52
  end
54
53
  end
@@ -58,7 +58,7 @@ module ElasticGraph
58
58
  # extra memory allocation and GC for the hash.
59
59
  arg_defn = arg_defns.find do |a|
60
60
  a.keyword == key
61
- end || raise(SchemaError, "Cannot find an argument definition for #{key.inspect} on `#{args_owner.name}`")
61
+ end || raise(Errors::SchemaError, "Cannot find an argument definition for #{key.inspect} on `#{args_owner.name}`")
62
62
 
63
63
  next_owner = arg_defn.type.unwrap
64
64
  accumulator[arg_defn.name] = to_schema_form(value, next_owner)
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # frozen_string_literal: true
8
8
 
9
- require "elastic_graph/error"
9
+ require "elastic_graph/errors"
10
10
 
11
11
  module ElasticGraph
12
12
  class GraphQL
@@ -15,7 +15,7 @@ module ElasticGraph
15
15
  class EnumValue < ::Data.define(:name, :type, :runtime_metadata)
16
16
  def sort_clauses
17
17
  sort_clause = runtime_metadata&.sort_field&.then { |sf| {sf.field_path => {"order" => sf.direction.to_s}} } ||
18
- raise(SchemaError, "Runtime metadata provides no `sort_field` for #{type.name}.#{name} enum value.")
18
+ raise(Errors::SchemaError, "Runtime metadata provides no `sort_field` for #{type.name}.#{name} enum value.")
19
19
 
20
20
  [sort_clause]
21
21
  end
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # frozen_string_literal: true
8
8
 
9
- require "elastic_graph/error"
9
+ require "elastic_graph/errors"
10
10
  require "elastic_graph/graphql/schema/relation_join"
11
11
  require "elastic_graph/graphql/schema/arguments"
12
12
 
@@ -126,7 +126,7 @@ module ElasticGraph
126
126
  def sort_argument_type
127
127
  @sort_argument_type ||= begin
128
128
  graphql_argument = @graphql_field.arguments.fetch(schema_element_names.order_by) do
129
- raise SchemaError, "`#{schema_element_names.order_by}` argument not defined for field `#{parent_type.name}.#{name}`."
129
+ raise Errors::SchemaError, "`#{schema_element_names.order_by}` argument not defined for field `#{parent_type.name}.#{name}`."
130
130
  end
131
131
  @schema.type_from(graphql_argument.type.unwrap)
132
132
  end
@@ -7,7 +7,7 @@
7
7
  # frozen_string_literal: true
8
8
 
9
9
  require "elastic_graph/datastore_core/index_definition"
10
- require "elastic_graph/error"
10
+ require "elastic_graph/errors"
11
11
  require "elastic_graph/graphql/schema/field"
12
12
  require "elastic_graph/graphql/schema/enum_value"
13
13
  require "forwardable"
@@ -118,7 +118,7 @@ module ElasticGraph
118
118
  rescue KeyError => e
119
119
  msg = "No field named #{field_name} (on type #{name}) could be found"
120
120
  msg += "; Possible alternatives: [#{e.corrections.join(", ").delete('"')}]." if e.corrections.any?
121
- raise NotFoundError, msg
121
+ raise Errors::NotFoundError, msg
122
122
  end
123
123
 
124
124
  def enum_value_named(enum_value_name)
@@ -233,7 +233,7 @@ module ElasticGraph
233
233
  rescue KeyError => e
234
234
  msg = "No enum value named #{enum_value_name} (on type #{name}) could be found"
235
235
  msg += "; Possible alternatives: [#{e.corrections.join(", ").delete('"')}]." if e.corrections.any?
236
- raise NotFoundError, msg
236
+ raise Errors::NotFoundError, msg
237
237
  end
238
238
 
239
239
  def build_fields_by_name_hash(schema, graphql_type)
@@ -10,7 +10,7 @@ require "digest/md5"
10
10
  require "forwardable"
11
11
  require "graphql"
12
12
  require "elastic_graph/constants"
13
- require "elastic_graph/error"
13
+ require "elastic_graph/errors"
14
14
  require "elastic_graph/graphql/monkey_patches/schema_field"
15
15
  require "elastic_graph/graphql/monkey_patches/schema_object"
16
16
  require "elastic_graph/graphql/schema/field"
@@ -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)
@@ -88,7 +91,7 @@ module ElasticGraph
88
91
  if index_definition_name.include?(ROLLOVER_INDEX_INFIX_MARKER)
89
92
  raise ArgumentError, "`#{index_definition_name}` is the name of a rollover index; pass the name of the parent index definition instead."
90
93
  else
91
- raise NotFoundError, "The index definition `#{index_definition_name}` does not appear to exist. Is it misspelled?"
94
+ raise Errors::NotFoundError, "The index definition `#{index_definition_name}` does not appear to exist. Is it misspelled?"
92
95
  end
93
96
  end
94
97
  end
@@ -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,31 +135,21 @@ 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 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
152
149
  @indexed_document_types_by_index_definition_name ||= indexed_document_types.each_with_object({}) do |type, hash|
153
150
  type.index_definitions.each do |index_def|
154
151
  if hash.key?(index_def.name)
155
- raise SchemaError, "DatastoreCore::IndexDefinition #{index_def.name} is used multiple times: #{type} vs #{hash[index_def.name]}"
152
+ raise Errors::SchemaError, "DatastoreCore::IndexDefinition #{index_def.name} is used multiple times: #{type} vs #{hash[index_def.name]}"
156
153
  end
157
154
 
158
155
  hash[index_def.name] = type
@@ -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,14 +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.4
4
+ version: 0.19.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Myron Marston
8
+ - Ben VandenBos
9
+ - Block Engineering
8
10
  autorequire:
9
11
  bindir: exe
10
12
  cert_chain: []
11
- date: 2024-09-06 00:00:00.000000000 Z
13
+ date: 2024-12-03 00:00:00.000000000 Z
12
14
  dependencies:
13
15
  - !ruby/object:Gem::Dependency
14
16
  name: rubocop-factory_bot
@@ -44,42 +46,42 @@ dependencies:
44
46
  requirements:
45
47
  - - "~>"
46
48
  - !ruby/object:Gem::Version
47
- version: '3.0'
49
+ version: '3.1'
48
50
  type: :development
49
51
  prerelease: false
50
52
  version_requirements: !ruby/object:Gem::Requirement
51
53
  requirements:
52
54
  - - "~>"
53
55
  - !ruby/object:Gem::Version
54
- version: '3.0'
56
+ version: '3.1'
55
57
  - !ruby/object:Gem::Dependency
56
58
  name: standard
57
59
  requirement: !ruby/object:Gem::Requirement
58
60
  requirements:
59
61
  - - "~>"
60
62
  - !ruby/object:Gem::Version
61
- version: 1.40.0
63
+ version: 1.41.0
62
64
  type: :development
63
65
  prerelease: false
64
66
  version_requirements: !ruby/object:Gem::Requirement
65
67
  requirements:
66
68
  - - "~>"
67
69
  - !ruby/object:Gem::Version
68
- version: 1.40.0
70
+ version: 1.41.0
69
71
  - !ruby/object:Gem::Dependency
70
72
  name: steep
71
73
  requirement: !ruby/object:Gem::Requirement
72
74
  requirements:
73
75
  - - "~>"
74
76
  - !ruby/object:Gem::Version
75
- version: '1.7'
77
+ version: '1.8'
76
78
  type: :development
77
79
  prerelease: false
78
80
  version_requirements: !ruby/object:Gem::Requirement
79
81
  requirements:
80
82
  - - "~>"
81
83
  - !ruby/object:Gem::Version
82
- version: '1.7'
84
+ version: '1.8'
83
85
  - !ruby/object:Gem::Dependency
84
86
  name: coderay
85
87
  requirement: !ruby/object:Gem::Requirement
@@ -134,14 +136,14 @@ dependencies:
134
136
  requirements:
135
137
  - - "~>"
136
138
  - !ruby/object:Gem::Version
137
- version: '0.12'
139
+ version: '0.13'
138
140
  type: :development
139
141
  prerelease: false
140
142
  version_requirements: !ruby/object:Gem::Requirement
141
143
  requirements:
142
144
  - - "~>"
143
145
  - !ruby/object:Gem::Version
144
- version: '0.12'
146
+ version: '0.13'
145
147
  - !ruby/object:Gem::Dependency
146
148
  name: simplecov
147
149
  requirement: !ruby/object:Gem::Requirement
@@ -252,126 +254,126 @@ dependencies:
252
254
  requirements:
253
255
  - - "~>"
254
256
  - !ruby/object:Gem::Version
255
- version: '3.4'
257
+ version: '3.5'
256
258
  type: :development
257
259
  prerelease: false
258
260
  version_requirements: !ruby/object:Gem::Requirement
259
261
  requirements:
260
262
  - - "~>"
261
263
  - !ruby/object:Gem::Version
262
- version: '3.4'
264
+ version: '3.5'
263
265
  - !ruby/object:Gem::Dependency
264
266
  name: elasticgraph-datastore_core
265
267
  requirement: !ruby/object:Gem::Requirement
266
268
  requirements:
267
269
  - - '='
268
270
  - !ruby/object:Gem::Version
269
- version: 0.18.0.4
271
+ version: 0.19.0.0.rc1
270
272
  type: :runtime
271
273
  prerelease: false
272
274
  version_requirements: !ruby/object:Gem::Requirement
273
275
  requirements:
274
276
  - - '='
275
277
  - !ruby/object:Gem::Version
276
- version: 0.18.0.4
278
+ version: 0.19.0.0.rc1
277
279
  - !ruby/object:Gem::Dependency
278
280
  name: elasticgraph-schema_artifacts
279
281
  requirement: !ruby/object:Gem::Requirement
280
282
  requirements:
281
283
  - - '='
282
284
  - !ruby/object:Gem::Version
283
- version: 0.18.0.4
285
+ version: 0.19.0.0.rc1
284
286
  type: :runtime
285
287
  prerelease: false
286
288
  version_requirements: !ruby/object:Gem::Requirement
287
289
  requirements:
288
290
  - - '='
289
291
  - !ruby/object:Gem::Version
290
- version: 0.18.0.4
292
+ version: 0.19.0.0.rc1
291
293
  - !ruby/object:Gem::Dependency
292
294
  name: graphql
293
295
  requirement: !ruby/object:Gem::Requirement
294
296
  requirements:
295
297
  - - "~>"
296
298
  - !ruby/object:Gem::Version
297
- version: 2.3.14
299
+ version: 2.4.5
298
300
  type: :runtime
299
301
  prerelease: false
300
302
  version_requirements: !ruby/object:Gem::Requirement
301
303
  requirements:
302
304
  - - "~>"
303
305
  - !ruby/object:Gem::Version
304
- version: 2.3.14
306
+ version: 2.4.5
305
307
  - !ruby/object:Gem::Dependency
306
308
  name: elasticgraph-admin
307
309
  requirement: !ruby/object:Gem::Requirement
308
310
  requirements:
309
311
  - - '='
310
312
  - !ruby/object:Gem::Version
311
- version: 0.18.0.4
313
+ version: 0.19.0.0.rc1
312
314
  type: :development
313
315
  prerelease: false
314
316
  version_requirements: !ruby/object:Gem::Requirement
315
317
  requirements:
316
318
  - - '='
317
319
  - !ruby/object:Gem::Version
318
- version: 0.18.0.4
320
+ version: 0.19.0.0.rc1
319
321
  - !ruby/object:Gem::Dependency
320
322
  name: elasticgraph-elasticsearch
321
323
  requirement: !ruby/object:Gem::Requirement
322
324
  requirements:
323
325
  - - '='
324
326
  - !ruby/object:Gem::Version
325
- version: 0.18.0.4
327
+ version: 0.19.0.0.rc1
326
328
  type: :development
327
329
  prerelease: false
328
330
  version_requirements: !ruby/object:Gem::Requirement
329
331
  requirements:
330
332
  - - '='
331
333
  - !ruby/object:Gem::Version
332
- version: 0.18.0.4
334
+ version: 0.19.0.0.rc1
333
335
  - !ruby/object:Gem::Dependency
334
336
  name: elasticgraph-opensearch
335
337
  requirement: !ruby/object:Gem::Requirement
336
338
  requirements:
337
339
  - - '='
338
340
  - !ruby/object:Gem::Version
339
- version: 0.18.0.4
341
+ version: 0.19.0.0.rc1
340
342
  type: :development
341
343
  prerelease: false
342
344
  version_requirements: !ruby/object:Gem::Requirement
343
345
  requirements:
344
346
  - - '='
345
347
  - !ruby/object:Gem::Version
346
- version: 0.18.0.4
348
+ version: 0.19.0.0.rc1
347
349
  - !ruby/object:Gem::Dependency
348
350
  name: elasticgraph-indexer
349
351
  requirement: !ruby/object:Gem::Requirement
350
352
  requirements:
351
353
  - - '='
352
354
  - !ruby/object:Gem::Version
353
- version: 0.18.0.4
355
+ version: 0.19.0.0.rc1
354
356
  type: :development
355
357
  prerelease: false
356
358
  version_requirements: !ruby/object:Gem::Requirement
357
359
  requirements:
358
360
  - - '='
359
361
  - !ruby/object:Gem::Version
360
- version: 0.18.0.4
362
+ version: 0.19.0.0.rc1
361
363
  - !ruby/object:Gem::Dependency
362
364
  name: elasticgraph-schema_definition
363
365
  requirement: !ruby/object:Gem::Requirement
364
366
  requirements:
365
367
  - - '='
366
368
  - !ruby/object:Gem::Version
367
- version: 0.18.0.4
369
+ version: 0.19.0.0.rc1
368
370
  type: :development
369
371
  prerelease: false
370
372
  version_requirements: !ruby/object:Gem::Requirement
371
373
  requirements:
372
374
  - - '='
373
375
  - !ruby/object:Gem::Version
374
- version: 0.18.0.4
376
+ version: 0.19.0.0.rc1
375
377
  description:
376
378
  email:
377
379
  - myron@squareup.com
@@ -459,10 +461,15 @@ files:
459
461
  - lib/elastic_graph/graphql/schema/type.rb
460
462
  - script/dump_time_zones
461
463
  - script/dump_time_zones.java
462
- homepage:
464
+ homepage: https://block.github.io/elasticgraph/
463
465
  licenses:
464
466
  - MIT
465
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
466
473
  gem_category: core
467
474
  post_install_message:
468
475
  rdoc_options: []