Class: ElasticGraph::SchemaDefinition::Factory
- Inherits:
-
Object
- Object
- ElasticGraph::SchemaDefinition::Factory
- Defined in:
- lib/elastic_graph/schema_definition/factory.rb
Overview
A class responsible for instantiating all schema elements. We want all schema element instantiation to go through this one class to support extension libraries. ElasticGraph supports extension libraries that provide modules that get extended onto specific instances of ElasticGraph framework classes. We prefer this approach rather than having extension library modules applied via ‘include` or `prepend`, because they _permanently modify_ the host classes. ElasticGraph is designed to avoid all mutable global state, and that includes mutations to ElasticGraph class ancestor chains from extension libraries.
Concretely, if we included or prepended extension libraries modules, we’d have a hard time keeping our tests order-independent and deterministic while running all the ElasticGraph test suites in the same Ruby process. A test using an extension library could cause a core ElasticGraph class to get mutated in a way that impacts a test that runs in the same process later. Instead, we expect extension libraries to hook into ElasticGraph using ‘extend` on particular object instances.
But that creates a bit of a problem: how can an extension library extend a module onto every instance of a specific type of schema element while it is in use? The answer is this factory class:
- An extension library can extend a module onto `schema.factory`.
- That module can in turn override any of these factory methods and extend another module onto the schema
element instances.
Constant Summary collapse
- @@deprecated_element_new =
prevent_non_factory_instantiation_of(SchemaElements::DeprecatedElement)
- @@argument_new =
prevent_non_factory_instantiation_of(SchemaElements::Argument)
- @@built_in_types_new =
prevent_non_factory_instantiation_of(SchemaElements::BuiltInTypes)
- @@directive_new =
prevent_non_factory_instantiation_of(SchemaElements::Directive)
- @@enum_type_new =
prevent_non_factory_instantiation_of(SchemaElements::EnumType)
- @@enum_value_new =
prevent_non_factory_instantiation_of(SchemaElements::EnumValue)
- @@enums_for_directly_queryable_types_new =
prevent_non_factory_instantiation_of(SchemaElements::EnumsForDirectlyQueryableTypes)
- @@field_new =
prevent_non_factory_instantiation_of(SchemaElements::Field)
- @@graphql_sdl_enumerator_new =
prevent_non_factory_instantiation_of(SchemaElements::GraphQLSDLEnumerator)
- @@input_field_new =
prevent_non_factory_instantiation_of(SchemaElements::InputField)
- @@input_type_new =
prevent_non_factory_instantiation_of(SchemaElements::InputType)
- @@interface_type_new =
prevent_non_factory_instantiation_of(SchemaElements::InterfaceType)
- @@object_type_new =
prevent_non_factory_instantiation_of(SchemaElements::ObjectType)
- @@scalar_type_new =
prevent_non_factory_instantiation_of(SchemaElements::ScalarType)
- @@sort_order_enum_value_new =
prevent_non_factory_instantiation_of(SchemaElements::SortOrderEnumValue)
- @@type_reference_new =
prevent_non_factory_instantiation_of(SchemaElements::TypeReference)
- @@type_with_subfields_new =
prevent_non_factory_instantiation_of(SchemaElements::TypeWithSubfields)
- @@union_type_new =
prevent_non_factory_instantiation_of(SchemaElements::UnionType)
- @@field_source_new =
prevent_non_factory_instantiation_of(SchemaElements::FieldSource)
- @@relationship_new =
prevent_non_factory_instantiation_of(SchemaElements::Relationship)
- @@index_new =
prevent_non_factory_instantiation_of(Indexing::Index)
- @@results_new =
prevent_non_factory_instantiation_of(Results)
- @@schema_artifact_manager_new =
prevent_non_factory_instantiation_of(SchemaArtifactManager)
Class Method Summary collapse
-
.prevent_non_factory_instantiation_of(klass) ⇒ Object
Helper method to help enforce our desired invariant: we want every instantiation of these schema element classes to happen via this factory method provided here.
Instance Method Summary collapse
- #build_relay_pagination_types(type_name, include_total_edge_count: false, derived_indexed_types: [], support_pagination: true, &customize_connection) ⇒ Object
-
#build_standard_filter_input_types_for_index_leaf_type(source_type, name_prefix: source_type, &define_filter_fields) ⇒ Object
Builds the standard set of filter input types for types which are indexing leaf types.
-
#build_standard_filter_input_types_for_index_object_type(source_type, name_prefix: source_type, &define_filter_fields) ⇒ Object
Builds the standard set of filter input types for types which are indexing object types.
-
#initialize(state) ⇒ Factory
constructor
A new instance of Factory.
-
#new_aggregated_values_type_for_index_leaf_type(index_leaf_type) ⇒ Object
Responsible for creating a new ‘*AggregatedValues` type for an index leaf type.
- #new_argument(field, name, value_type) ⇒ Object
- #new_built_in_types(api) ⇒ Object
- #new_deprecated_element(name, defined_at:, defined_via:) ⇒ Object
- #new_directive(name, arguments) ⇒ Object
- #new_enum_type(name, &block) ⇒ Object
- #new_enum_value(name, original_name) ⇒ Object
- #new_enums_for_directly_queryable_types ⇒ Object
- #new_field_source(relationship_name:, field_path:) ⇒ Object
- #new_filter_input_type(source_type, name_prefix: source_type, category: :filter_input) ⇒ Object
- #new_graphql_sdl_enumerator(all_types) ⇒ Object
- #new_index(name, settings, type, &block) ⇒ Object
- #new_input_type(name) ⇒ Object
- #new_interface_type(name) ⇒ Object
- #new_object_type(name) ⇒ Object
- #new_relationship(field, cardinality:, related_type:, foreign_key:, direction:) ⇒ Object
- #new_results ⇒ Object
- #new_scalar_type(name) ⇒ Object
- #new_schema_artifact_manager(schema_definition_results:, schema_artifacts_directory:, enforce_json_schema_version:, output:, max_diff_lines: 50) ⇒ Object
- #new_sort_order_enum_value(enum_value, sort_order_field_path) ⇒ Object
- #new_type_reference(name) ⇒ Object
- #new_type_with_subfields(schema_kind, name, wrapping_type:, field_factory:) ⇒ Object
- #new_union_type(name) ⇒ Object
Constructor Details
#initialize(state) ⇒ Factory
Returns a new instance of Factory.
61 62 63 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 61 def initialize(state) @state = state end |
Class Method Details
.prevent_non_factory_instantiation_of(klass) ⇒ Object
Helper method to help enforce our desired invariant: we want every instantiation of these schema element classes to happen via this factory method provided here. To enforce that, this helper returns the ‘new` method (as a `Method` object) after removing it from the given class. That makes it impossible for `new` to be called by anyone except from the factory using the captured method object.
69 70 71 72 73 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 69 def self.prevent_non_factory_instantiation_of(klass) klass.method(:new).tap do klass.singleton_class.undef_method :new end end |
Instance Method Details
#build_relay_pagination_types(type_name, include_total_edge_count: false, derived_indexed_types: [], support_pagination: true, &customize_connection) ⇒ Object
213 214 215 216 217 218 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 213 def build_relay_pagination_types(type_name, include_total_edge_count: false, derived_indexed_types: [], support_pagination: true, &customize_connection) [ (edge_type_for(type_name) if support_pagination), connection_type_for(type_name, include_total_edge_count, derived_indexed_types, support_pagination, &customize_connection) ].compact end |
#build_standard_filter_input_types_for_index_leaf_type(source_type, name_prefix: source_type, &define_filter_fields) ⇒ Object
Builds the standard set of filter input types for types which are indexing leaf types.
All GraphQL leaf types (enums and scalars) are indexing leaf types, but some GraphQL object types are as well. For example, ‘GeoLocation` is an object type in GraphQL (with separate lat/long fields) but is an indexing leaf type because we use the datastore `geo_point` type for it.
192 193 194 195 196 197 198 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 192 def build_standard_filter_input_types_for_index_leaf_type(source_type, name_prefix: source_type, &define_filter_fields) single_value_filter = new_filter_input_type(source_type, name_prefix: name_prefix, &define_filter_fields) list_filter = new_list_filter_input_type(source_type, name_prefix: name_prefix, any_satisfy_type_category: :list_element_filter_input) list_element_filter = new_list_element_filter_input_type(source_type, name_prefix: name_prefix, &define_filter_fields) [single_value_filter, list_filter, list_element_filter] end |
#build_standard_filter_input_types_for_index_object_type(source_type, name_prefix: source_type, &define_filter_fields) ⇒ Object
Builds the standard set of filter input types for types which are indexing object types.
Most GraphQL object types are indexing object types as well, but not all. For example, ‘GeoLocation` is an object type in GraphQL (with separate lat/long fields) but is an indexing leaf type because we use the datastore `geo_point` type for it.
205 206 207 208 209 210 211 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 205 def build_standard_filter_input_types_for_index_object_type(source_type, name_prefix: source_type, &define_filter_fields) single_value_filter = new_filter_input_type(source_type, name_prefix: name_prefix, &define_filter_fields) list_filter = new_list_filter_input_type(source_type, name_prefix: name_prefix, any_satisfy_type_category: :filter_input) fields_list_filter = new_fields_list_filter_input_type(source_type, name_prefix: name_prefix) [single_value_filter, list_filter, fields_list_filter] end |
#new_aggregated_values_type_for_index_leaf_type(index_leaf_type) ⇒ Object
Responsible for creating a new ‘*AggregatedValues` type for an index leaf type.
An index leaf type is a scalar, enum, object type that is backed by a single, indivisible field in the index. All scalar and enum types are index leaf types, and object types rarely (but sometimes) are. For example, the ‘GeoLocation` object type has two subfields (`latitude` and `longitude`) but is backed by a single `geo_point` field in the index, so it is an index leaf type.
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 315 def new_aggregated_values_type_for_index_leaf_type(index_leaf_type) new_object_type @state.type_ref(index_leaf_type).as_aggregated_values.name do |type| type.graphql_only true type.documentation "A return type used from aggregations to provided aggregated values over `#{index_leaf_type}` fields." type.resolve_fields_with :object_with_lookahead type.(elasticgraph_category: :scalar_aggregated_values) type.field @state.schema_elements.approximate_distinct_value_count, "JsonSafeLong", graphql_only: true do |f| # Note: the 1-6% accuracy figure comes from the Elasticsearch docs: # https://www.elastic.co/guide/en/elasticsearch/reference/8.10/search-aggregations-metrics-cardinality-aggregation.html#_counts_are_approximate f.documentation <<~EOS An approximation of the number of unique values for this field within this grouping. The approximation uses the HyperLogLog++ algorithm from the [HyperLogLog in Practice](https://research.google.com/pubs/archive/40671.pdf) paper. The accuracy of the returned value varies based on the specific dataset, but it usually differs from the true distinct value count by less than 7%. EOS f. empty_bucket_value: 0, function: :cardinality end yield type end end |
#new_argument(field, name, value_type) ⇒ Object
80 81 82 83 84 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 80 def new_argument(field, name, value_type) @@argument_new.call(@state, field, name, value_type).tap do |argument| yield argument if block_given? end end |
#new_built_in_types(api) ⇒ Object
87 88 89 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 87 def new_built_in_types(api) @@built_in_types_new.call(api, @state) end |
#new_deprecated_element(name, defined_at:, defined_via:) ⇒ Object
75 76 77 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 75 def new_deprecated_element(name, defined_at:, defined_via:) @@deprecated_element_new.call(schema_def_state: @state, name: name, defined_at: defined_at, defined_via: defined_via) end |
#new_directive(name, arguments) ⇒ Object
92 93 94 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 92 def new_directive(name, arguments) @@directive_new.call(name, arguments) end |
#new_enum_type(name, &block) ⇒ Object
97 98 99 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 97 def new_enum_type(name, &block) @@enum_type_new.call(@state, name, &(_ = block)) end |
#new_enum_value(name, original_name) ⇒ Object
102 103 104 105 106 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 102 def new_enum_value(name, original_name) @@enum_value_new.call(@state, name, original_name) do |enum_value| yield enum_value if block_given? end end |
#new_enums_for_directly_queryable_types ⇒ Object
109 110 111 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 109 def new_enums_for_directly_queryable_types @@enums_for_directly_queryable_types_new.call(@state) end |
#new_field_source(relationship_name:, field_path:) ⇒ Object
265 266 267 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 265 def new_field_source(relationship_name:, field_path:) @@field_source_new.call(relationship_name, field_path) end |
#new_filter_input_type(source_type, name_prefix: source_type, category: :filter_input) ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 142 def new_filter_input_type(source_type, name_prefix: source_type, category: :filter_input) all_of = @state.schema_elements.all_of any_of = @state.schema_elements.any_of new_input_type(@state.type_ref(name_prefix).as_static_derived_type(category).name) do |t| t.documentation <<~EOS Input type used to specify filters on `#{source_type}` fields. Will match all documents if passed as an empty object (or as `null`). EOS t.field @state.schema_elements.any_of, "[#{t.name}!]" do |f| f.documentation <<~EOS Matches records where any of the provided sub-filters evaluate to true. This works just like an OR operator in SQL. When `null` is passed, matches all documents. When an empty list is passed, this part of the filter matches no documents. EOS end t.field @state.schema_elements.all_of, "[#{t.name}!]" do |f| f.documentation <<~EOS Matches records where all of the provided sub-filters evaluate to true. This works just like an AND operator in SQL. Note: multiple filters are automatically ANDed together. This is only needed when you have multiple filters that can't be provided on a single `#{t.name}` input because of collisions between key names. For example, if you want to AND multiple OR'd sub-filters (the equivalent of (A OR B) AND (C OR D)), you could do #{all_of}: [{#{any_of}: [...]}, {#{any_of}: [...]}]. When `null` or an empty list is passed, matches all documents. EOS end t.field @state.schema_elements.not, t.name do |f| f.documentation <<~EOS Matches records where the provided sub-filter evaluates to false. This works just like a NOT operator in SQL. When `null` or an empty object is passed, matches no documents. EOS end yield t end end |
#new_graphql_sdl_enumerator(all_types) ⇒ Object
121 122 123 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 121 def new_graphql_sdl_enumerator(all_types) @@graphql_sdl_enumerator_new.call(@state, all_types) end |
#new_index(name, settings, type, &block) ⇒ Object
281 282 283 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 281 def new_index(name, settings, type, &block) @@index_new.call(name, settings, @state, type, &block) end |
#new_input_type(name) ⇒ Object
135 136 137 138 139 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 135 def new_input_type(name) @@input_type_new.call(@state, name) do |input_type| yield input_type end end |
#new_interface_type(name) ⇒ Object
220 221 222 223 224 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 220 def new_interface_type(name) @@interface_type_new.call(@state, name.to_s) do |interface_type| yield interface_type end end |
#new_object_type(name) ⇒ Object
227 228 229 230 231 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 227 def new_object_type(name) @@object_type_new.call(@state, name.to_s) do |object_type| yield object_type if block_given? end end |
#new_relationship(field, cardinality:, related_type:, foreign_key:, direction:) ⇒ Object
270 271 272 273 274 275 276 277 278 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 270 def new_relationship(field, cardinality:, related_type:, foreign_key:, direction:) @@relationship_new.call( field, cardinality: cardinality, related_type: , foreign_key: foreign_key, direction: direction ) end |
#new_results ⇒ Object
286 287 288 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 286 def new_results @@results_new.call(@state) end |
#new_scalar_type(name) ⇒ Object
234 235 236 237 238 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 234 def new_scalar_type(name) @@scalar_type_new.call(@state, name.to_s) do |scalar_type| yield scalar_type end end |
#new_schema_artifact_manager(schema_definition_results:, schema_artifacts_directory:, enforce_json_schema_version:, output:, max_diff_lines: 50) ⇒ Object
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 291 def new_schema_artifact_manager( schema_definition_results:, schema_artifacts_directory:, enforce_json_schema_version:, output:, max_diff_lines: 50 ) @@schema_artifact_manager_new.call( schema_definition_results:, schema_artifacts_directory:, enforce_json_schema_version:, output:, max_diff_lines: ) end |
#new_sort_order_enum_value(enum_value, sort_order_field_path) ⇒ Object
241 242 243 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 241 def new_sort_order_enum_value(enum_value, sort_order_field_path) @@sort_order_enum_value_new.call(enum_value, sort_order_field_path) end |
#new_type_reference(name) ⇒ Object
246 247 248 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 246 def new_type_reference(name) @@type_reference_new.call(name, @state) end |
#new_type_with_subfields(schema_kind, name, wrapping_type:, field_factory:) ⇒ Object
251 252 253 254 255 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 251 def new_type_with_subfields(schema_kind, name, wrapping_type:, field_factory:) @@type_with_subfields_new.call(schema_kind, @state, name, wrapping_type: wrapping_type, field_factory: field_factory) do |type_with_subfields| yield type_with_subfields end end |
#new_union_type(name) ⇒ Object
258 259 260 261 262 |
# File 'lib/elastic_graph/schema_definition/factory.rb', line 258 def new_union_type(name) @@union_type_new.call(@state, name.to_s) do |union_type| yield union_type end end |