@@ -79,13 +79,59 @@ def schema_entities
7979 # infinite recursion
8080 types_schema . orphan_types ( original_query )
8181
82- # Walk through all of the types and determine which ones are entities (any type with a
83- # "key" directive)
84- types_schema . send ( :non_introspection_types ) . values . flatten . select do |type |
85- # TODO: Find Objects that implement interfaces that are entities. Make sure they are also entities.
86- ( type . include? ( ApolloFederation ::Object ) || type . include? ( ApolloFederation ::Interface ) ) &&
87- type . federation_directives &.any? { |directive | directive [ :name ] == 'key' }
82+ entities_collection , federation_entities , interface_types_map = collect_entitites ( types_schema )
83+
84+ if federation_entities . any?
85+ entity_names = entities_collection . map ( &:graphql_name )
86+
87+ federation_entities . each do |interface |
88+ members = interface_types_map . fetch ( interface . graphql_name , [ ] )
89+ not_entity_members = members . reject { |member | entity_names . include? ( member ) }
90+
91+ # If all interface members are entities, it is valid so we add it to the collection
92+ if not_entity_members . empty?
93+ entities_collection << interface
94+ else
95+ raise "Interface #{ interface . graphql_name } is not valid. " \
96+ "Types `#{ not_entity_members . join ( ', ' ) } ` do not have a @key directive. " \
97+ 'All types that implement an interface with a @key directive must also have a @key directive.'
98+ end
99+ end
88100 end
101+
102+ entities_collection
103+ end
104+
105+ # Walk through all of the types and interfaces and determine which ones are entities
106+ # (any type with a "key" directive)
107+ # However, for interface entities, don't add them straight away, but first check that
108+ # all implementing types of the interfaces are also entities.
109+ def collect_entitites ( types_schema )
110+ federation_entities = [ ]
111+ interface_types_map = { }
112+
113+ entities_collection = types_schema . send ( :non_introspection_types ) . values . flatten . select do |type |
114+ # keep track of the interfaces -> type relations.
115+ if type . respond_to? ( :implements )
116+ type . implements . each do |interface |
117+ interface_types_map [ interface . abstract_type . graphql_name ] ||= [ ]
118+ interface_types_map [ interface . abstract_type . graphql_name ] << type . graphql_name
119+ end
120+ end
121+
122+ # Only add Type entities to the collection
123+ # Interface entities will be added later if all implementing types are entities
124+ if type . include? ( ApolloFederation ::Object ) && includes_key_directive? ( type )
125+ true
126+ elsif type . include? ( ApolloFederation ::Interface ) && includes_key_directive? ( type )
127+ federation_entities << type
128+ false
129+ else
130+ false
131+ end
132+ end
133+
134+ [ entities_collection , federation_entities , interface_types_map ]
89135 end
90136
91137 def federation_query ( query_obj )
@@ -110,6 +156,10 @@ def federation_query(query_obj)
110156 klass . define_service_field
111157 klass
112158 end
159+
160+ def includes_key_directive? ( type )
161+ type . federation_directives &.any? { |directive | directive [ :name ] == 'key' }
162+ end
113163 end
114164 end
115165end
0 commit comments