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:



54
55
56
# File 'lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb', line 54

def implemented_interfaces
  @implemented_interfaces ||= []
end

#implements(*interface_names) ⇒ void

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



43
44
45
46
47
48
49
50
51
# File 'lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb', line 43

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|
      schema_def_state.implementations_by_interface_ref[interface_ref] << self
    end
  end

  implemented_interfaces.concat(interface_refs)
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



109
110
111
112
113
114
115
116
117
118
# File 'lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb', line 109

def to_sdl(&field_arg_selector)
  name_section =
    if implemented_interfaces.empty?
      name
    else
      "#{name} implements #{implemented_interfaces.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.



73
74
75
76
77
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
# File 'lib/elastic_graph/schema_definition/mixins/implements_interfaces.rb', line 73

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