Class: ElasticGraph::SchemaDefinition::SchemaElements::TypeReference

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/elastic_graph/schema_definition/schema_elements/type_reference.rb

Overview

Represents a reference to a type. This is basically just a name of a type, with the ability to resolve it to an actual type object on demand. In addition, we provide some useful logic that is based entirely on the type name.

This is necessary because GraphQL does not require that types are defined before they are referenced. (And also you can have circular type dependencies). Therefore, we need to use a reference to a type initially, and can later resolve it to a concrete type object as needed.

Constant Summary collapse

STATIC_FORMAT_NAME_BY_CATEGORY =

Most of ElasticGraph’s derived GraphQL types have a static suffix (e.g. the full type name is source_type + suffix). This is a map of all of these.

TypeNamer::REQUIRED_PLACEHOLDERS.filter_map do |format_name, placeholders|
  if placeholders == [:base]
    as_snake_case = SchemaArtifacts::RuntimeMetadata::SchemaElementNamesDefinition::SnakeCaseConverter
      .normalize_case(format_name.to_s)
      .delete_prefix("_")

    [as_snake_case.to_sym, format_name]
  end
end.to_h

Instance Method Summary collapse

Instance Method Details

#as_aggregation_sub_aggregations(parent_doc_types: [fully_unwrapped.name], field_path: []) ⇒ Object

Generates the type name used for a ‘sub_aggregations` field. A `sub_aggregations` field is available alongside `grouped_by`, `count`, and `aggregated_values` on an aggregation or sub-aggregation node. This type is used in two situations:

  1. It is used directly under ‘nodes`/`edges { node }` on an Aggregation or SubAggregation. It provides access to each of the sub-aggregations that are available in that context.

  2. It is used underneath that ‘SubAggregations` object for single object fields which have fields under them that are sub-aggregatable.

The fields (and types of those fields) used for one of these types is contextual based on what the parent doc types are (so that we can offer sub-aggregations of the parent doc types!) and the field path (for the 2nd case).



252
253
254
255
256
257
258
259
260
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 252

def as_aggregation_sub_aggregations(parent_doc_types: [fully_unwrapped.name], field_path: [])
  field_part = field_path.map { |f| to_title_case(f.name) }.join

  renamed_with_same_wrappings(type_namer.generate_name_for(
    :SubAggregations,
    parent_agg_type: parent_aggregation_type(parent_doc_types),
    field_path: field_part
  ))
end

#as_object_typeObject

Returns the ‘ObjectType`, `UnionType` or `InterfaceType` object to which this type name refers, if it is the name of one of those kinds of types.

Ignores any non-null wrapping on the type, if there is one.



60
61
62
63
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 60

def as_object_type
  type = _ = unwrap_non_null.resolved
  type if type.respond_to?(:graphql_fields_by_name)
end

#as_parent_aggregation(parent_doc_types:) ⇒ Object



262
263
264
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 262

def as_parent_aggregation(parent_doc_types:)
  schema_def_state.type_ref(parent_aggregation_type(parent_doc_types))
end

#as_static_derived_type(category) ⇒ Object

Builds a ‘TypeReference` for a statically named derived type for the given `category.

In addition, a dynamic method ‘as_` is also provided (defined further below).



217
218
219
220
221
222
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 217

def as_static_derived_type(category)
  renamed_with_same_wrappings(type_namer.generate_name_for(
    STATIC_FORMAT_NAME_BY_CATEGORY.fetch(category),
    base: fully_unwrapped.name
  ))
end

#as_sub_aggregation(parent_doc_types:) ⇒ Object

Generates the type name used for a sub-aggregation. This type has ‘grouped_by`, `aggregated_values`, `count` and `sub_aggregations` sub-fields to expose the different bits of aggregation functionality.

The type name is based both on the type reference name and on the set of ‘parent_doc_types` that exist above it. The `parent_doc_types` are used in the name because we plan to offer different sub-aggregations under it based on where it is in the document structure. A type which is `nested` at multiple levels in different document contexts needs separate types generated for each case so that we can offer the correct contextual sub-aggregations that can be offered for each case.



232
233
234
235
236
237
238
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 232

def as_sub_aggregation(parent_doc_types:)
  renamed_with_same_wrappings(type_namer.generate_name_for(
    :SubAggregation,
    base: fully_unwrapped.name,
    parent_types: parent_doc_types.join
  ))
end

#boolean?Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 138

def boolean?
  name == "Boolean"
end

#enum?Boolean

Returns:

  • (Boolean)


95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 95

def enum?
  return unwrap_non_null.enum? if non_null?

  if (resolved_type = resolved)
    return resolved_type.is_a?(EnumType)
  end

  # For derived GraphQL types, the name usually implies what kind of type it is.
  # The derived types get generated last, so this prediate may be called before the
  # type has been defined.
  case schema_kind_implied_by_name
  when :object
    false
  when :enum
    true
  else
    # If we can't determine the type from the name, just raise an error.
    raise Errors::SchemaError, "Type `#{name}` cannot be resolved. Is it misspelled?"
  end
end

#fully_unwrappedObject

Extracts the type without any non-null or list wrappings it has.



35
36
37
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 35

def fully_unwrapped
  schema_def_state.type_ref(unwrapped_name)
end

#json_schema_layersObject

Returns all the JSON schema array/nullable layers of a type, from outermost to innermost. For example, [[Int]] will return [:nullable, :array, :nullable, :array, :nullable]



171
172
173
174
175
176
177
178
179
180
181
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 171

def json_schema_layers
  @json_schema_layers ||= begin
    layers, inner_type = peel_json_schema_layers_once

    if layers.empty? || inner_type == self
      layers
    else
      layers + inner_type.json_schema_layers
    end
  end
end

#leaf?Boolean

Returns ‘true` if this is known to be a scalar type or enum type. Returns `false` if this is known to be an object type or list type of any sort.

Raises an error if it cannot be determined either from the name or by resolving the type.

Ignores any non-null wrapping on the type, if there is one.

Returns:

  • (Boolean)


122
123
124
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 122

def leaf?
  !list? && !object?
end

#list?Boolean

Returns ‘true` if this is a list type.

Ignores any non-null wrapping on the type, if there is one.

Returns:

  • (Boolean)


129
130
131
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 129

def list?
  name.start_with?("[")
end

#list_element_filter_input?Boolean

Returns:

  • (Boolean)


278
279
280
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 278

def list_element_filter_input?
  matches_format_of?(:list_element_filter_input)
end

#list_filter_input?Boolean

Returns:

  • (Boolean)


274
275
276
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 274

def list_filter_input?
  matches_format_of?(:list_filter_input)
end

#non_null?Boolean

Returns ‘true` if this is a non-null type.

Returns:

  • (Boolean)


134
135
136
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 134

def non_null?
  name.end_with?("!")
end

#object?Boolean

Returns ‘true` if this is known to be an object type of some sort (including interface types, union types, and proper object types).

Returns ‘false` if this is known to be a leaf type of some sort (either a scalar or enum). Returns `false` if this is a list type (either a list of objects or leafs).

Raises an error if it cannot be determined either from the name or by resolving the type.

Ignores any non-null wrapping on the type, if there is one.

Returns:

  • (Boolean)


74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 74

def object?
  return unwrap_non_null.object? if non_null?

  if (resolved_type = resolved)
    return resolved_type.respond_to?(:graphql_fields_by_name)
  end

  # For derived GraphQL types, the name usually implies what kind of type it is.
  # The derived types get generated last, so this prediate may be called before the
  # type has been defined.
  case schema_kind_implied_by_name
  when :object
    true
  when :enum
    false
  else
    # If we can't determine the type from the name, just raise an error.
    raise Errors::SchemaError, "Type `#{name}` cannot be resolved. Is it misspelled?"
  end
end

#resolvedObject



146
147
148
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 146

def resolved
  schema_def_state.types_by_name[name]
end

#scalar_type_needing_grouped_by_object?Boolean

Generally speaking, scalar types have ‘grouped_by` fields which are scalars of the same types, and object types have `grouped_by` fields which are special `[object_type]GroupedBy` types.

…except for some special cases (Date and DateTime), which this predicate detects.

Returns:

  • (Boolean)


160
161
162
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 160

def scalar_type_needing_grouped_by_object?
  %w[Date DateTime].include?(type_namer.revert_override_for(name))
end

#to_final_form(as_input: false) ⇒ Object

Converts the TypeReference to its final form (i.e. the from that will be used in rendered schema artifacts). This handles multiple bits of type name customization based on the configured ‘type_name_overrides` and `derived_type_name_formats` settings (via the `TypeNamer`):

  • If the ‘as_input` is `true` and this is a reference to an enum type, converts to the `InputEnum` format.

  • If there is a configured name override that applies to this type, uses it.



201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 201

def to_final_form(as_input: false)
  unwrapped = fully_unwrapped
  inner_name = type_namer.name_for(unwrapped.name)

  if as_input && schema_def_state.type_ref(inner_name).enum?
    inner_name = type_namer.name_for(
      type_namer.generate_name_for(:InputEnum, base: inner_name)
    )
  end

  renamed_with_same_wrappings(inner_name)
end

#to_sObject



142
143
144
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 142

def to_s
  name
end

#unwrap_listObject

Removes the list wrapping if this is a list.

If the outer wrapping is non-null, unwraps that as well.



52
53
54
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 52

def unwrap_list
  schema_def_state.type_ref(unwrap_non_null.name.delete_prefix("[").delete_suffix("]"))
end

#unwrap_non_nullObject

Removes any non-null wrappings the type has.



40
41
42
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 40

def unwrap_non_null
  schema_def_state.type_ref(name.delete_suffix("!"))
end

#unwrapped_nameObject



150
151
152
153
154
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 150

def unwrapped_name
  name
    .sub(/\A\[+/, "") # strip `[` characters from the start: https://rubular.com/r/tHVBBQkQUMMVVz
    .sub(/[\]!]+\z/, "") # strip `]` and `!` characters from the end: https://rubular.com/r/pC8C0i7EpvHDbf
end

#with_reverted_overrideObject

Returns a new ‘TypeReference` with any type name overrides reverted (to provide the “original” type name).



165
166
167
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 165

def with_reverted_override
  schema_def_state.type_ref(type_namer.revert_override_for(name))
end

#wrap_non_nullObject



44
45
46
47
# File 'lib/elastic_graph/schema_definition/schema_elements/type_reference.rb', line 44

def wrap_non_null
  return self if non_null?
  schema_def_state.type_ref("#{name}!")
end