elasticgraph-schema_definition 0.19.1.0 → 0.19.2.0

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/lib/elastic_graph/schema_definition/api.rb +111 -6
  4. data/lib/elastic_graph/schema_definition/factory.rb +25 -23
  5. data/lib/elastic_graph/schema_definition/indexing/derived_fields/append_only_set.rb +1 -1
  6. data/lib/elastic_graph/schema_definition/indexing/derived_fields/field_initializer_support.rb +1 -1
  7. data/lib/elastic_graph/schema_definition/indexing/derived_fields/immutable_value.rb +1 -1
  8. data/lib/elastic_graph/schema_definition/indexing/derived_fields/min_or_max_value.rb +1 -1
  9. data/lib/elastic_graph/schema_definition/indexing/derived_indexed_type.rb +1 -1
  10. data/lib/elastic_graph/schema_definition/indexing/event_envelope.rb +12 -3
  11. data/lib/elastic_graph/schema_definition/indexing/field.rb +1 -1
  12. data/lib/elastic_graph/schema_definition/indexing/field_reference.rb +1 -1
  13. data/lib/elastic_graph/schema_definition/indexing/field_type/enum.rb +1 -1
  14. data/lib/elastic_graph/schema_definition/indexing/field_type/object.rb +1 -1
  15. data/lib/elastic_graph/schema_definition/indexing/field_type/scalar.rb +1 -1
  16. data/lib/elastic_graph/schema_definition/indexing/field_type/union.rb +1 -1
  17. data/lib/elastic_graph/schema_definition/indexing/index.rb +4 -2
  18. data/lib/elastic_graph/schema_definition/indexing/json_schema_field_metadata.rb +1 -1
  19. data/lib/elastic_graph/schema_definition/indexing/json_schema_with_metadata.rb +1 -1
  20. data/lib/elastic_graph/schema_definition/indexing/list_counts_mapping.rb +1 -1
  21. data/lib/elastic_graph/schema_definition/indexing/relationship_resolver.rb +1 -1
  22. data/lib/elastic_graph/schema_definition/indexing/rollover_config.rb +1 -1
  23. data/lib/elastic_graph/schema_definition/indexing/update_target_factory.rb +1 -1
  24. data/lib/elastic_graph/schema_definition/indexing/update_target_resolver.rb +1 -1
  25. data/lib/elastic_graph/schema_definition/json_schema_pruner.rb +4 -2
  26. data/lib/elastic_graph/schema_definition/mixins/can_be_graphql_only.rb +1 -1
  27. data/lib/elastic_graph/schema_definition/mixins/has_derived_graphql_type_customizations.rb +1 -1
  28. data/lib/elastic_graph/schema_definition/mixins/has_directives.rb +1 -1
  29. data/lib/elastic_graph/schema_definition/mixins/has_documentation.rb +1 -1
  30. data/lib/elastic_graph/schema_definition/mixins/has_indices.rb +40 -4
  31. data/lib/elastic_graph/schema_definition/mixins/has_readable_to_s_and_inspect.rb +2 -1
  32. data/lib/elastic_graph/schema_definition/mixins/has_subtypes.rb +1 -1
  33. data/lib/elastic_graph/schema_definition/mixins/has_type_info.rb +1 -1
  34. data/lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb +1 -1
  35. data/lib/elastic_graph/schema_definition/mixins/supports_default_value.rb +1 -1
  36. data/lib/elastic_graph/schema_definition/mixins/supports_filtering_and_aggregation.rb +9 -3
  37. data/lib/elastic_graph/schema_definition/mixins/verifies_graphql_name.rb +1 -1
  38. data/lib/elastic_graph/schema_definition/rake_tasks.rb +1 -1
  39. data/lib/elastic_graph/schema_definition/results.rb +133 -13
  40. data/lib/elastic_graph/schema_definition/schema_artifact_manager.rb +4 -4
  41. data/lib/elastic_graph/schema_definition/schema_elements/argument.rb +1 -1
  42. data/lib/elastic_graph/schema_definition/schema_elements/built_in_types.rb +47 -15
  43. data/lib/elastic_graph/schema_definition/schema_elements/deprecated_element.rb +1 -1
  44. data/lib/elastic_graph/schema_definition/schema_elements/directive.rb +1 -1
  45. data/lib/elastic_graph/schema_definition/schema_elements/enum_type.rb +1 -1
  46. data/lib/elastic_graph/schema_definition/schema_elements/enum_value.rb +1 -1
  47. data/lib/elastic_graph/schema_definition/schema_elements/enum_value_namer.rb +1 -1
  48. data/lib/elastic_graph/schema_definition/schema_elements/enums_for_indexed_types.rb +1 -1
  49. data/lib/elastic_graph/schema_definition/schema_elements/field.rb +15 -10
  50. data/lib/elastic_graph/schema_definition/schema_elements/field_path.rb +1 -1
  51. data/lib/elastic_graph/schema_definition/schema_elements/field_source.rb +1 -1
  52. data/lib/elastic_graph/schema_definition/schema_elements/graphql_sdl_enumerator.rb +4 -78
  53. data/lib/elastic_graph/schema_definition/schema_elements/input_field.rb +1 -1
  54. data/lib/elastic_graph/schema_definition/schema_elements/input_type.rb +6 -2
  55. data/lib/elastic_graph/schema_definition/schema_elements/interface_type.rb +1 -1
  56. data/lib/elastic_graph/schema_definition/schema_elements/list_counts_state.rb +1 -1
  57. data/lib/elastic_graph/schema_definition/schema_elements/object_type.rb +1 -1
  58. data/lib/elastic_graph/schema_definition/schema_elements/relationship.rb +8 -2
  59. data/lib/elastic_graph/schema_definition/schema_elements/scalar_type.rb +3 -3
  60. data/lib/elastic_graph/schema_definition/schema_elements/sort_order_enum_value.rb +1 -1
  61. data/lib/elastic_graph/schema_definition/schema_elements/sub_aggregation_path.rb +1 -1
  62. data/lib/elastic_graph/schema_definition/schema_elements/type_namer.rb +1 -1
  63. data/lib/elastic_graph/schema_definition/schema_elements/type_reference.rb +1 -1
  64. data/lib/elastic_graph/schema_definition/schema_elements/type_with_subfields.rb +4 -5
  65. data/lib/elastic_graph/schema_definition/schema_elements/union_type.rb +1 -1
  66. data/lib/elastic_graph/schema_definition/scripting/file_system_repository.rb +1 -1
  67. data/lib/elastic_graph/schema_definition/scripting/script.rb +1 -1
  68. data/lib/elastic_graph/schema_definition/scripting/scripts/update/index_data.painless +0 -1
  69. data/lib/elastic_graph/schema_definition/state.rb +4 -1
  70. data/lib/elastic_graph/schema_definition/test_support.rb +23 -3
  71. metadata +32 -30
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -45,7 +45,8 @@ module ElasticGraph
45
45
  [type] + schema_def_state.factory.build_relay_pagination_types(type.name, support_pagination: false) do |t|
46
46
  # Record metadata that is necessary for elasticgraph-graphql to correctly recognize and handle
47
47
  # this sub-aggregation correctly.
48
- t.runtime_metadata_overrides = {elasticgraph_category: :nested_sub_aggregation_connection}
48
+ t.override_runtime_metadata(elasticgraph_category: :nested_sub_aggregation_connection)
49
+ t.default_graphql_resolver = :object_without_lookahead
49
50
  end
50
51
  end
51
52
 
@@ -116,6 +117,7 @@ module ElasticGraph
116
117
  schema_def_state.sub_aggregation_paths_for(nested_field_ref.parent_type).map do |path|
117
118
  schema_def_state.factory.new_object_type type_ref.as_sub_aggregation(parent_doc_types: path.parent_doc_types).name do |t|
118
119
  t.documentation "Return type representing a bucket of `#{name}` objects for a sub-aggregation within each `#{type_ref.as_parent_aggregation(parent_doc_types: path.parent_doc_types).name}`."
120
+ t.default_graphql_resolver = :object_without_lookahead
119
121
 
120
122
  t.field schema_def_state.schema_elements.count_detail, "AggregationCountDetail", graphql_only: true do |f|
121
123
  f.documentation "Details of the count of `#{name}` documents in a sub-aggregation bucket."
@@ -163,6 +165,7 @@ module ElasticGraph
163
165
  schema_def_state.factory.new_object_type agg_sub_aggs_type_ref.name do |t|
164
166
  under_field_description = "under `#{path.field_path_string}` " unless path.field_path.empty?
165
167
  t.documentation "Provides access to the `#{schema_def_state.schema_elements.sub_aggregations}` #{under_field_description}within each `#{type_ref.as_parent_aggregation(parent_doc_types: path.parent_doc_types).name}`."
168
+ t.default_graphql_resolver = :object_with_lookahead
166
169
 
167
170
  sub_aggregatable_fields.each do |field|
168
171
  if field.nested?
@@ -225,7 +228,8 @@ module ElasticGraph
225
228
 
226
229
  # Record metadata that is necessary for elasticgraph-graphql to correctly recognize and handle
227
230
  # this indexed aggregation type correctly.
228
- t.runtime_metadata_overrides = {source_type: name, elasticgraph_category: :indexed_aggregation}
231
+ t.default_graphql_resolver = :object_without_lookahead
232
+ t.override_runtime_metadata(source_type: name, elasticgraph_category: :indexed_aggregation)
229
233
  end
230
234
  end
231
235
 
@@ -236,6 +240,7 @@ module ElasticGraph
236
240
 
237
241
  new_non_empty_object_type type_ref.as_grouped_by.name do |t|
238
242
  t.documentation "Type used to specify the `#{name}` fields to group by for aggregations."
243
+ t.default_graphql_resolver = :object_with_lookahead
239
244
 
240
245
  graphql_fields_by_name.values.each do |field|
241
246
  field.define_grouped_by_field(t)
@@ -250,6 +255,7 @@ module ElasticGraph
250
255
 
251
256
  new_non_empty_object_type type_ref.as_aggregated_values.name do |t|
252
257
  t.documentation "Type used to perform aggregation computations on `#{name}` fields."
258
+ t.default_graphql_resolver = :object_with_lookahead
253
259
 
254
260
  graphql_fields_by_name.values.each do |field|
255
261
  field.define_aggregated_values_field(t)
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -110,6 +110,64 @@ module ElasticGraph
110
110
  # Record that we are now generating results so that caching can kick in.
111
111
  state.user_definition_complete = true
112
112
  state.user_definition_complete_callbacks.each(&:call)
113
+ define_root_graphql_type
114
+ end
115
+
116
+ def define_root_graphql_type
117
+ state.api.object_type "Query" do |query_type|
118
+ query_type.documentation "The query entry point for the entire schema."
119
+ query_type.default_graphql_resolver = nil
120
+
121
+ state.types_by_name.values.select(&:indexed?).sort_by(&:name).each do |type|
122
+ # @type var indexed_type: Mixins::HasIndices & _Type
123
+ indexed_type = _ = type
124
+
125
+ query_type.relates_to_many(
126
+ indexed_type.plural_root_query_field_name,
127
+ indexed_type.name,
128
+ via: "ignore",
129
+ dir: :in,
130
+ singular: indexed_type.singular_root_query_field_name
131
+ ) do |f|
132
+ f.documentation "Fetches `#{indexed_type.name}`s based on the provided arguments."
133
+ f.resolver = :list_records
134
+ f.hide_relationship_runtime_metadata = true
135
+ indexed_type.root_query_fields_customizations&.call(f)
136
+ end
137
+
138
+ # Add additional efficiency hints to the aggregation field documentation if we have any such hints.
139
+ # This needs to be outside the `relates_to_many` block because `relates_to_many` adds its own "suffix" to
140
+ # the field documentation, and here we add another one.
141
+ if (agg_efficiency_hint = aggregation_efficiency_hints_for(indexed_type.derived_indexed_types))
142
+ agg_name = state.schema_elements.normalize_case("#{indexed_type.singular_root_query_field_name}_aggregations")
143
+ agg_field = query_type.graphql_fields_by_name.fetch(agg_name)
144
+ agg_field.documentation "#{agg_field.doc_comment}\n\n#{agg_efficiency_hint}"
145
+ end
146
+ end
147
+
148
+ state.built_in_types_customization_blocks.each do |customization_block|
149
+ customization_block.call(query_type)
150
+ end
151
+ end
152
+ end
153
+
154
+ def aggregation_efficiency_hints_for(derived_indexed_types)
155
+ return nil if derived_indexed_types.empty?
156
+
157
+ hints = derived_indexed_types.map do |type|
158
+ derived_indexing_type = state.types_by_name.fetch(type.destination_type_ref.name)
159
+ alternate_field_name = (_ = derived_indexing_type).plural_root_query_field_name
160
+ grouping_field = type.id_source
161
+
162
+ " - The root `#{alternate_field_name}` field groups by `#{grouping_field}`"
163
+ end
164
+
165
+ <<~EOS
166
+ Note: aggregation queries are relatively expensive, and some fields have been pre-aggregated to allow
167
+ more efficient queries for some common aggregation cases:
168
+
169
+ #{hints.join("\n")}
170
+ EOS
113
171
  end
114
172
 
115
173
  def json_schema_with_metadata_merger
@@ -144,7 +202,7 @@ module ElasticGraph
144
202
  def build_runtime_metadata
145
203
  extra_update_targets_by_object_type_name = identify_extra_update_targets_by_object_type_name
146
204
 
147
- object_types_by_name = all_types_except_root_query_type
205
+ object_types_by_name = all_types
148
206
  .select { |t| t.respond_to?(:graphql_fields_by_name) }
149
207
  .to_h { |type| [type.name, (_ = type).runtime_metadata(extra_update_targets_by_object_type_name.fetch(type.name) { [] })] }
150
208
 
@@ -157,7 +215,7 @@ module ElasticGraph
157
215
  .filter_map { |type| enum_generator.sort_order_enum_for(_ = type) }
158
216
  .to_h { |enum_type| [(_ = enum_type).name, (_ = enum_type).runtime_metadata] }
159
217
 
160
- enum_types_by_name = all_types_except_root_query_type
218
+ enum_types_by_name = all_types
161
219
  .grep(SchemaElements::EnumType) # : ::Array[SchemaElements::EnumType]
162
220
  .to_h { |t| [t.name, t.runtime_metadata] }
163
221
  .merge(indexed_enum_types_by_name)
@@ -173,8 +231,9 @@ module ElasticGraph
173
231
  index_definitions_by_name: index_definitions_by_name,
174
232
  schema_element_names: state.schema_elements,
175
233
  graphql_extension_modules: state.graphql_extension_modules,
234
+ graphql_resolvers_by_name: state.graphql_resolvers_by_name,
176
235
  static_script_ids_by_scoped_name: STATIC_SCRIPT_REPO.script_ids_by_scoped_name
177
- )
236
+ ).tap { |rm| verify_runtime_metadata(rm) }
178
237
  end
179
238
 
180
239
  # Builds a map, keyed by object type name, of extra `update_targets` that have been generated
@@ -183,7 +242,9 @@ module ElasticGraph
183
242
  sourced_field_errors = [] # : ::Array[::String]
184
243
  relationship_errors = [] # : ::Array[::String]
185
244
 
186
- state.object_types_by_name.values.each_with_object(::Hash.new { |h, k| h[k] = [] }) do |object_type, accum|
245
+ state.object_types_by_name.except("Query").values.each_with_object(
246
+ ::Hash.new { |h, k| h[k] = [] } # : ::Hash[untyped, ::Array[SchemaArtifacts::RuntimeMetadata::UpdateTarget]]
247
+ ) do |object_type, accum|
187
248
  fields_with_sources_by_relationship_name =
188
249
  if object_type.indices.empty?
189
250
  # only indexed types can have `sourced_from` fields, and resolving `fields_with_sources` on an unindexed union type
@@ -251,7 +312,7 @@ module ElasticGraph
251
312
  state.object_types_by_name.values.each(&:verify_graphql_correctness!)
252
313
 
253
314
  type_defs = state.factory
254
- .new_graphql_sdl_enumerator(all_types_except_root_query_type)
315
+ .new_graphql_sdl_enumerator(all_types)
255
316
  .map { |sdl| strip_trailing_whitespace(sdl) }
256
317
 
257
318
  [type_defs + state.sdl_parts].join("\n\n")
@@ -283,7 +344,9 @@ module ElasticGraph
283
344
 
284
345
  def json_schema_indexing_field_types_by_name
285
346
  @json_schema_indexing_field_types_by_name ||= state
286
- .types_by_name.values
347
+ .types_by_name
348
+ .except("Query")
349
+ .values
287
350
  .reject do |t|
288
351
  derived_indexing_type_names.include?(t.name) ||
289
352
  # Skip graphql framework types
@@ -293,6 +356,54 @@ module ElasticGraph
293
356
  .to_h { |type| [type.name, type.to_indexing_field_type] }
294
357
  end
295
358
 
359
+ def verify_runtime_metadata(runtime_metadata)
360
+ registered_resolvers = runtime_metadata.graphql_resolvers_by_name
361
+
362
+ fields_by_resolvers = ::Hash.new { |h, k| h[k] = [] } # : ::Hash[::Symbol, ::Array[::String]]
363
+ runtime_metadata
364
+ .object_types_by_name
365
+ .each do |type_name, type|
366
+ type.graphql_fields_by_name.each do |field_name, field|
367
+ if (resolver = field.resolver)
368
+ fields_by_resolvers[resolver] << "#{type_name}.#{field_name}"
369
+ end
370
+ end
371
+ end
372
+
373
+ unused_resolvers = registered_resolvers.except(*fields_by_resolvers.keys).reject do |name, res|
374
+ # Ignore our built-in resolvers.
375
+ res.resolver_ref.fetch("require_path").start_with?("elastic_graph/graphql/resolvers/")
376
+ end.keys
377
+
378
+ unless unused_resolvers.empty?
379
+ state.output.puts <<~EOS
380
+ WARNING: #{unused_resolvers.size} GraphQL resolver(s) have been registered but are unused:
381
+ - #{unused_resolvers.sort.join("\n - ")}
382
+ These resolvers can be removed.
383
+ EOS
384
+ end
385
+
386
+ undefined_resolvers = fields_by_resolvers.except(*registered_resolvers.keys)
387
+ unless undefined_resolvers.empty?
388
+ resolver_errors = undefined_resolvers.sort_by(&:first).map do |resolver, fields|
389
+ <<~EOS.strip
390
+ GraphQL resolver `#{resolver.inspect}` is unregistered but is assigned to #{fields.size} field(s):
391
+
392
+ - #{fields.sort.join("\n - ")}
393
+ EOS
394
+ end
395
+
396
+ raise Errors::SchemaError, <<~EOS
397
+ #{resolver_errors.join("\n\n")}
398
+
399
+ To continue, register the named resolvers with `schema.register_graphql_resolver`
400
+ or update the fields listed above to use one of the other registered resolvers:
401
+
402
+ - #{registered_resolvers.keys.map(&:inspect).sort.join("\n - ")}
403
+ EOS
404
+ end
405
+ end
406
+
296
407
  def strip_trailing_whitespace(string)
297
408
  string.gsub(/ +$/, "")
298
409
  end
@@ -300,14 +411,17 @@ module ElasticGraph
300
411
  def check_for_circular_dependencies!
301
412
  return if @no_circular_dependencies
302
413
 
303
- referenced_types_by_source_type = state.types_by_name
414
+ referenced_types_by_source_type = ::Hash.new { |h, k| h[k] = ::Set.new } # : ::Hash[::String, ::Set[::String]]
415
+ state.types_by_name
304
416
  .reject { |_, type| type.graphql_only? }
305
- .each_with_object(::Hash.new { |h, k| h[k] = ::Set.new }) do |(type_name, _), cache|
306
- recursively_add_referenced_types_to(state.type_ref(type_name), cache)
417
+ .each do |type_name, _|
418
+ recursively_add_referenced_types_to(state.type_ref(type_name), referenced_types_by_source_type)
307
419
  end
308
420
 
309
421
  circular_reference_sets = referenced_types_by_source_type
422
+ # standard:disable Style/HashSlice -- https://github.com/rubocop/rubocop/issues/13885
310
423
  .select { |source_type, referenced_types| referenced_types.include?(source_type) }
424
+ # standard:enable Style/HashSlice
311
425
  .values
312
426
  .uniq
313
427
 
@@ -341,9 +455,15 @@ module ElasticGraph
341
455
  end
342
456
  end
343
457
 
344
- def all_types_except_root_query_type
345
- @all_types_except_root_query_type ||= state.types_by_name.values.flat_map do |registered_type|
346
- related_types = [registered_type] + registered_type.derived_graphql_types
458
+ def all_types
459
+ @all_types ||= state.types_by_name.values.flat_map do |registered_type|
460
+ related_types =
461
+ if registered_type.name == "Query"
462
+ [registered_type]
463
+ else
464
+ [registered_type] + registered_type.derived_graphql_types
465
+ end
466
+
347
467
  apply_customizations_to(related_types, registered_type)
348
468
  related_types
349
469
  end
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -178,9 +178,9 @@ module ElasticGraph
178
178
  end
179
179
 
180
180
  def report_json_schema_merge_errors(merged_results)
181
- json_schema_versions_by_missing_field = ::Hash.new { |h, k| h[k] = [] }
182
- json_schema_versions_by_missing_type = ::Hash.new { |h, k| h[k] = [] }
183
- json_schema_versions_by_missing_necessary_field = ::Hash.new { |h, k| h[k] = [] }
181
+ json_schema_versions_by_missing_field = ::Hash.new { |h, k| h[k] = [] } # : ::Hash[::String, ::Array[::Integer]]
182
+ json_schema_versions_by_missing_type = ::Hash.new { |h, k| h[k] = [] } # : ::Hash[::String, ::Array[::Integer]]
183
+ json_schema_versions_by_missing_necessary_field = ::Hash.new { |h, k| h[k] = [] } # : ::Hash[Indexing::JSONSchemaWithMetadata::MissingNecessaryField, ::Array[::Integer]]
184
184
 
185
185
  merged_results.each do |result|
186
186
  result.missing_fields.each do |field|
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -181,6 +181,7 @@ module ElasticGraph
181
181
  register_enum_types
182
182
  register_date_and_time_grouped_by_types
183
183
  register_standard_elastic_graph_types
184
+ register_standard_graphql_resolvers
184
185
  end
185
186
 
186
187
  private
@@ -281,11 +282,11 @@ module ElasticGraph
281
282
  f.default false
282
283
  end
283
284
 
284
- # any_of/not don't really make sense on this filter because it doesn't make sense to
285
- # apply an OR operator or negation to the fields of this type since they are all an
285
+ # any_of/all_of/not don't really make sense on this filter because it doesn't make sense
286
+ # to apply an OR operator or negation to the fields of this type since they are all an
286
287
  # indivisible part of a single filter operation on a specific field. So we remove them
287
288
  # here.
288
- remove_any_of_and_not_filter_operators_on(t)
289
+ remove_any_of_and_all_of_and_not_filter_operators_on(t)
289
290
  end
290
291
 
291
292
  register_filter "MatchesPhrase" do |t|
@@ -299,11 +300,11 @@ module ElasticGraph
299
300
  f.documentation "The input phrase to search for."
300
301
  end
301
302
 
302
- # any_of/not don't really make sense on this filter because it doesn't make sense to
303
- # apply an OR operator or negation to the fields of this type since they are all an
303
+ # any_of/all_of/not don't really make sense on this filter because it doesn't make
304
+ # to apply an OR operator or negation to the fields of this type since they are all an
304
305
  # indivisible part of a single filter operation on a specific field. So we remove them
305
306
  # here.
306
- remove_any_of_and_not_filter_operators_on(t)
307
+ remove_any_of_and_all_of_and_not_filter_operators_on(t)
307
308
  end
308
309
 
309
310
  # This is defined as a built-in ElasticGraph type so that we can leverage Elasticsearch/OpenSearch GeoLocation features
@@ -371,11 +372,11 @@ module ElasticGraph
371
372
  f.documentation "Determines the unit of the specified `#{names.max_distance}`."
372
373
  end
373
374
 
374
- # any_of/not don't really make sense on this filter because it doesn't make sense to
375
- # apply an OR operator or negation to the fields of this type since they are all an
375
+ # any_of/all_of/not don't really make sense on this filter because it doesn't make sense
376
+ # to apply an OR operator or negation to the fields of this type since they are all an
376
377
  # indivisible part of a single filter operation on a specific field. So we remove them
377
378
  # here.
378
- remove_any_of_and_not_filter_operators_on(t)
379
+ remove_any_of_and_all_of_and_not_filter_operators_on(t)
379
380
  end
380
381
 
381
382
  # Note: `has_next_page`/`has_previous_page` are required to be non-null by the relay
@@ -393,6 +394,8 @@ module ElasticGraph
393
394
  # to go from non-null to null, but is not a breaking change to make it non-null
394
395
  # in the future.
395
396
  register_framework_object_type "PageInfo" do |t|
397
+ t.default_graphql_resolver = :object_without_lookahead
398
+
396
399
  t.documentation <<~EOS
397
400
  Provides information about the specific fetched page. This implements the `PageInfo`
398
401
  specification from the [Relay GraphQL Cursor Connections
@@ -509,6 +512,7 @@ module ElasticGraph
509
512
 
510
513
  register_framework_object_type "AggregationCountDetail" do |t|
511
514
  t.documentation "Provides detail about an aggregation `#{names.count}`."
515
+ t.default_graphql_resolver = :object_without_lookahead
512
516
 
513
517
  t.field names.approximate_value, "JsonSafeLong!", graphql_only: true do |f|
514
518
  f.documentation <<~EOS
@@ -728,11 +732,11 @@ module ElasticGraph
728
732
  f.default "UTC"
729
733
  end
730
734
 
731
- # With our initial implementation of `time_of_day` filtering, it's tricky to support `any_of`/`not` within
735
+ # With our initial implementation of `time_of_day` filtering, it's tricky to support `any_of`/`all_of`/`not` within
732
736
  # the `time_of_day: {...}` input object. They are still supported outside of `time_of_day` (on the parent
733
737
  # input object) so no functionality is losts by omitting these. Also, this aligns with our `GeoLocationDistanceFilterInput`
734
738
  # which is a similarly complex filter where we didn't include them.
735
- remove_any_of_and_not_filter_operators_on(t)
739
+ remove_any_of_and_all_of_and_not_filter_operators_on(t)
736
740
  end
737
741
  end
738
742
 
@@ -1290,7 +1294,8 @@ module ElasticGraph
1290
1294
  date = schema_def_state.type_ref("Date")
1291
1295
  register_framework_object_type date.as_grouped_by.name do |t|
1292
1296
  t.documentation "Allows for grouping `Date` values based on the desired return type."
1293
- t.runtime_metadata_overrides = {elasticgraph_category: :date_grouped_by_object}
1297
+ t.default_graphql_resolver = :object_with_lookahead
1298
+ t.override_runtime_metadata(elasticgraph_category: :date_grouped_by_object)
1294
1299
 
1295
1300
  t.field names.as_date, "Date", graphql_only: true do |f|
1296
1301
  f.documentation "Used when grouping on the full `Date` value."
@@ -1307,7 +1312,8 @@ module ElasticGraph
1307
1312
  date_time = schema_def_state.type_ref("DateTime")
1308
1313
  register_framework_object_type date_time.as_grouped_by.name do |t|
1309
1314
  t.documentation "Allows for grouping `DateTime` values based on the desired return type."
1310
- t.runtime_metadata_overrides = {elasticgraph_category: :date_grouped_by_object}
1315
+ t.default_graphql_resolver = :object_with_lookahead
1316
+ t.override_runtime_metadata(elasticgraph_category: :date_grouped_by_object)
1311
1317
 
1312
1318
  t.field names.as_date_time, "DateTime", graphql_only: true do |f|
1313
1319
  f.documentation "Used when grouping on the full `DateTime` value."
@@ -1363,6 +1369,31 @@ module ElasticGraph
1363
1369
  end
1364
1370
  end
1365
1371
 
1372
+ def register_standard_graphql_resolvers
1373
+ require(require_path = "elastic_graph/graphql/resolvers/get_record_field_value")
1374
+ schema_def_api.register_graphql_resolver :get_record_field_value,
1375
+ GraphQL::Resolvers::GetRecordFieldValue,
1376
+ defined_at: require_path
1377
+
1378
+ require(require_path = "elastic_graph/graphql/resolvers/list_records")
1379
+ schema_def_api.register_graphql_resolver :list_records,
1380
+ GraphQL::Resolvers::ListRecords,
1381
+ defined_at: require_path
1382
+
1383
+ require(require_path = "elastic_graph/graphql/resolvers/nested_relationships")
1384
+ schema_def_api.register_graphql_resolver :nested_relationships,
1385
+ GraphQL::Resolvers::NestedRelationships,
1386
+ defined_at: require_path
1387
+
1388
+ require(require_path = "elastic_graph/graphql/resolvers/object")
1389
+ schema_def_api.register_graphql_resolver :object_with_lookahead,
1390
+ GraphQL::Resolvers::Object::WithLookahead,
1391
+ defined_at: require_path
1392
+ schema_def_api.register_graphql_resolver :object_without_lookahead,
1393
+ GraphQL::Resolvers::Object::WithoutLookahead,
1394
+ defined_at: require_path
1395
+ end
1396
+
1366
1397
  def define_date_grouping_arguments(grouping_field, omit_timezone: false)
1367
1398
  define_calendar_type_grouping_arguments(grouping_field, schema_def_state.type_ref("Date"), <<~EOS, omit_timezone: omit_timezone)
1368
1399
  For example, when grouping by `WEEK`, you can shift by 1 day to change what day-of-week weeks are considered to start on.
@@ -1515,8 +1546,9 @@ module ElasticGraph
1515
1546
  schema_def_state.register_input_type(input_type)
1516
1547
  end
1517
1548
 
1518
- def remove_any_of_and_not_filter_operators_on(type)
1549
+ def remove_any_of_and_all_of_and_not_filter_operators_on(type)
1519
1550
  type.graphql_fields_by_name.delete(names.any_of)
1551
+ type.graphql_fields_by_name.delete(names.all_of)
1520
1552
  type.graphql_fields_by_name.delete(names.not)
1521
1553
  end
1522
1554
  end
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -92,7 +92,7 @@ module ElasticGraph
92
92
  :aggregated_values_customizations, :sort_order_enum_value_customizations,
93
93
  :args, :sortable, :filterable, :aggregatable, :groupable, :graphql_only, :source, :runtime_field_script, :relationship, :singular_name,
94
94
  :computation_detail, :non_nullable_in_json_schema, :backing_indexing_field, :as_input,
95
- :legacy_grouping_schema, :name_in_index
95
+ :legacy_grouping_schema, :name_in_index, :resolver
96
96
  )
97
97
  include Mixins::HasDocumentation
98
98
  include Mixins::HasDirectives
@@ -103,10 +103,9 @@ module ElasticGraph
103
103
  def initialize(
104
104
  name:, type:, parent_type:, schema_def_state:,
105
105
  accuracy_confidence: :high, name_in_index: name,
106
- runtime_metadata_graphql_field: SchemaArtifacts::RuntimeMetadata::GraphQLField::EMPTY,
107
106
  type_for_derived_types: nil, graphql_only: nil, singular: nil,
108
107
  sortable: nil, filterable: nil, aggregatable: nil, groupable: nil,
109
- backing_indexing_field: nil, as_input: false, legacy_grouping_schema: false
108
+ backing_indexing_field: nil, as_input: false, legacy_grouping_schema: false, resolver: nil
110
109
  )
111
110
  type_ref = schema_def_state.type_ref(type)
112
111
  super(
@@ -137,10 +136,11 @@ module ElasticGraph
137
136
  non_nullable_in_json_schema: false,
138
137
  backing_indexing_field: backing_indexing_field,
139
138
  as_input: as_input,
140
- legacy_grouping_schema: legacy_grouping_schema
139
+ legacy_grouping_schema: legacy_grouping_schema,
140
+ resolver: resolver
141
141
  )
142
142
 
143
- if name != name_in_index && name_in_index&.include?(".") && !graphql_only
143
+ if name != name_in_index && name_in_index.include?(".") && !graphql_only
144
144
  raise Errors::SchemaError, "#{self} has an invalid `name_in_index`: #{name_in_index.inspect}. Only `graphql_only: true` fields can have a `name_in_index` that references a child field."
145
145
  end
146
146
 
@@ -735,9 +735,13 @@ module ElasticGraph
735
735
  .as_static_derived_type(filter_field_category(for_single_value))
736
736
  .name
737
737
 
738
- params = to_h
739
- .slice(*@@initialize_param_names)
740
- .merge(type: filter_type, parent_type: parent_type, name_in_index: name_in_index, type_for_derived_types: nil)
738
+ params = to_h.slice(*@@initialize_param_names).merge(
739
+ type: filter_type,
740
+ parent_type: parent_type,
741
+ name_in_index: name_in_index,
742
+ type_for_derived_types: nil,
743
+ resolver: nil
744
+ )
741
745
 
742
746
  schema_def_state.factory.new_field(**params).tap do |f|
743
747
  f.documentation derived_documentation(
@@ -925,7 +929,8 @@ module ElasticGraph
925
929
  SchemaArtifacts::RuntimeMetadata::GraphQLField.new(
926
930
  name_in_index: name_in_index,
927
931
  computation_detail: computation_detail,
928
- relation: relationship&.runtime_metadata
932
+ relation: relationship&.runtime_metadata,
933
+ resolver: resolver
929
934
  )
930
935
  end
931
936
 
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Block, Inc.
1
+ # Copyright 2024 - 2025 Block, Inc.
2
2
  #
3
3
  # Use of this source code is governed by an MIT-style
4
4
  # license that can be found in the LICENSE file or at