Module: ElasticGraph::SchemaDefinition::Mixins::ImplementsInterfaces

Included in:
SchemaElements::InterfaceType, SchemaElements::ObjectType
Defined in:
lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb

Overview

Mixin for types that can implement interfaces (SchemaElements::ObjectType and SchemaElements::InterfaceType).

Instance Method Summary collapse

Instance Method Details

#implemented_interfacesArray<SchemaElements::TypeReference>

Returns list of type references for the interface types implemented by this type.

Returns:



59
60
61
# File 'lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb', line 59

def implemented_interfaces
  @implemented_interfaces ||= []
end

#implements(*interface_names) ⇒ void

Note:

If the named interface has declared an index (via HasIndices#index), calling ‘implements` causes this type to automatically inherit that index — it will be stored in the same datastore index as all other implementations of the named interface. To use a dedicated index instead, call HasIndices#index on this type.

This method returns an undefined value.

Declares that the current type implements the specified interface, making the current type a subtype of the interface. The current type must define all of the fields of the named interface, with the exact same field types.

Examples:

Implement an interface

ElasticGraph.define_schema do |schema|
  schema.interface_type "Athlete" do |t|
    t.field "name", "String"
    t.field "team", "String"
  end

  schema.object_type "BaseballPlayer" do |t|
    t.implements "Athlete"
    t.field "name", "String"
    t.field "team", "String"
    t.field "battingAvg", "Float"
  end

  schema.object_type "BasketballPlayer" do |t|
    t.implements "Athlete"
    t.field "name", "String"
    t.field "team", "String"
    t.field "pointsPerGame", "Float"
  end
end

Parameters:

  • interface_names (Array<String>)

    names of interface types implemented by this type



47
48
49
50
51
52
53
54
55
56
# File 'lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb', line 47

def implements(*interface_names)
  interface_refs = interface_names.map do |interface_name|
    schema_def_state.type_ref(interface_name).to_final_form.tap do |interface_ref|
      implementations = schema_def_state.implementations_by_interface_ref[interface_ref] # : ::Set[SchemaElements::TypeWithSubfields]
      implementations << self
    end
  end

  implemented_interfaces.concat(interface_refs)
end

#recursively_resolve_supertypesSet<UnionType, InterfaceType>

Returns all supertypes of this type, including union memberships and interface ancestors.

Returns:

  • (Set<UnionType, InterfaceType>)

    set of supertypes



131
132
133
134
# File 'lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb', line 131

def recursively_resolve_supertypes
  union_memberships = schema_def_state.union_types_by_member_ref[type_ref] # : ::Set[abstractType]
  union_memberships | recursively_resolve_interface_supertypes
end

#to_sdl {|SchemaElements::Argument| ... } ⇒ String

Returns SDL string of the type.

Yields:

Yield Returns:

  • (Boolean)

    whether or not to include the argument in the generated GraphQL SDL

Returns:

  • (String)

    SDL string of the type



114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb', line 114

def to_sdl(&field_arg_selector)
  name_section =
    if implemented_interfaces.empty?
      name
    else
      # Include all ancestor interfaces in SDL
      all_interfaces = recursively_resolve_supertypes.grep(SchemaElements::InterfaceType)
      "#{name} implements #{all_interfaces.map(&:name).sort.join(" & ")}"
    end

  generate_sdl(name_section: name_section, &field_arg_selector)
end

#verify_graphql_correctness!void

This method returns an undefined value.

Called after the schema definition is complete, before dumping artifacts. Here we validate the correctness of interface implementations. We defer it until this time to not require the interface and fields to be defined before the ‘implements` call.

Note that the GraphQL gem on its own supports a form of “interface inheritance”: if declaring that an object type implements an interface, and the object type is missing one or more of the interface fields, the GraphQL gem dynamically adds the missing interface fields to the object type (at least, that’s the result I noted when dumping the GraphQL SDL after trying that!). However, we cannot allow that, because our schema definition is used to generate non-GrapQL artifacts (e.g. the JSON schema and the index mapping), and all the artifacts must agree on the fields. Therefore, we use this method to verify that the object type fully implements the specified interfaces.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb', line 78

def verify_graphql_correctness!
  schema_error_messages = implemented_interfaces.filter_map do |interface_ref|
    interface = interface_ref.resolved

    case interface
    when SchemaElements::InterfaceType
      differences = (_ = interface).interface_fields_by_name.values.filter_map do |interface_field|
        my_field_sdl = graphql_fields_by_name[interface_field.name]&.to_sdl(type_structure_only: true)
        interface_field_sdl = interface_field.to_sdl(type_structure_only: true)

        if my_field_sdl.nil?
          "missing `#{interface_field.name}`"
        elsif my_field_sdl != interface_field_sdl
          "`#{interface_field_sdl.strip}` vs `#{my_field_sdl.strip}`"
        end
      end

      unless differences.empty?
        "Type `#{name}` does not correctly implement interface `#{interface_ref}` " \
          "due to field differences: #{differences.join("; ")}."
      end
    when nil
      "Type `#{name}` cannot implement `#{interface_ref}` because `#{interface_ref}` is not defined."
    else
      "Type `#{name}` cannot implement `#{interface_ref}` because `#{interface_ref}` is not an interface."
    end
  end

  unless schema_error_messages.empty?
    raise Errors::SchemaError, schema_error_messages.join("\n\n")
  end
end