elasticgraph-graphql 0.18.0.4 → 0.19.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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: []