-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Basic support for virtual fields using hydration (#600)
* Big hack * Delete more hacks * Replace hardcoded virtualType * Delete dead code * Delete unused comment * Add extra types in test and ignore more virtual types in validation * Add scalar test * Address some PR comments * Remove virtual type context from batch hydration * Fix FF * Add simple test * Add failing test due to new FF * Fix test * Rename * Delete unused * Refactor * Reformat * Additional tests & FF
- Loading branch information
Showing
31 changed files
with
1,999 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
153 changes: 153 additions & 0 deletions
153
lib/src/main/java/graphql/nadel/engine/blueprint/NadelVirtualTypeBlueprintFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package graphql.nadel.engine.blueprint | ||
|
||
import graphql.nadel.engine.blueprint.directives.getHydratedOrNull | ||
import graphql.nadel.engine.blueprint.directives.isHydration | ||
import graphql.nadel.engine.util.getFieldAt | ||
import graphql.nadel.engine.util.unwrapAll | ||
import graphql.schema.GraphQLFieldDefinition | ||
import graphql.schema.GraphQLObjectType | ||
import graphql.schema.GraphQLSchema | ||
|
||
internal class NadelVirtualTypeBlueprintFactory { | ||
private data class FactoryContext( | ||
val engineSchema: GraphQLSchema, | ||
) | ||
|
||
fun makeVirtualTypeContext( | ||
engineSchema: GraphQLSchema, | ||
containerType: GraphQLObjectType, | ||
virtualFieldDef: GraphQLFieldDefinition, | ||
): NadelVirtualTypeContext? { | ||
val factoryContext = FactoryContext( | ||
engineSchema, | ||
) | ||
|
||
return with(factoryContext) { | ||
makeVirtualTypeContext(containerType, virtualFieldDef) | ||
} | ||
} | ||
|
||
context(FactoryContext) | ||
private fun makeVirtualTypeContext( | ||
containerType: GraphQLObjectType, | ||
virtualFieldDef: GraphQLFieldDefinition, | ||
): NadelVirtualTypeContext? { | ||
val typeMappings = createTypeMappings(virtualFieldDef) | ||
|
||
return if (typeMappings.isEmpty()) { | ||
return null | ||
} else { | ||
NadelVirtualTypeContext( | ||
virtualFieldContainer = containerType, | ||
virtualField = virtualFieldDef, | ||
virtualTypeToBackingType = typeMappings.virtualTypeToBackingTypeMap(), | ||
backingTypeToVirtualType = typeMappings.backingTypeToVirtualTypeMap(), | ||
) | ||
} | ||
} | ||
|
||
context(FactoryContext) | ||
private fun createTypeMappings( | ||
virtualFieldDef: GraphQLFieldDefinition, | ||
): List<VirtualTypeMapping> { | ||
val hydration = virtualFieldDef.getHydratedOrNull() | ||
?: return emptyList() // We should use the non-null method once we delete @hydratedFrom | ||
val backingFieldDef = engineSchema.queryType.getFieldAt(hydration.backingField)!! | ||
val backingType = backingFieldDef.type.unwrapAll() as? GraphQLObjectType | ||
?: return emptyList() | ||
val virtualType = virtualFieldDef.type.unwrapAll() as? GraphQLObjectType | ||
?: return emptyList() | ||
|
||
// Not a virtual type | ||
if (virtualType.name == backingType.name) { | ||
return emptyList() | ||
} | ||
|
||
return createTypeMappings( | ||
backingType = backingType, | ||
virtualType = virtualType, | ||
) | ||
} | ||
|
||
context(FactoryContext) | ||
private fun createTypeMappings( | ||
backingType: GraphQLObjectType, | ||
virtualType: GraphQLObjectType, | ||
): List<VirtualTypeMapping> { | ||
return listOf(VirtualTypeMapping(virtualType, backingType)) + createTypeMappings( | ||
virtualType = virtualType, | ||
backingType = backingType, | ||
visitedVirtualTypes = mutableSetOf(virtualType.name), | ||
) | ||
} | ||
|
||
context(FactoryContext) | ||
private fun createTypeMappings( | ||
virtualType: GraphQLObjectType, | ||
backingType: GraphQLObjectType, | ||
visitedVirtualTypes: MutableSet<String>, | ||
): List<VirtualTypeMapping> { | ||
return virtualType | ||
.fields | ||
.flatMap { virtualFieldDef -> | ||
val virtualOutputType = virtualFieldDef.type.unwrapAll() as? GraphQLObjectType | ||
?: return@flatMap emptyList() | ||
|
||
if (visitedVirtualTypes.visit(virtualOutputType.name)) { | ||
val backingFieldDef = backingType.getField(virtualFieldDef.name) | ||
val backingOutputType = backingFieldDef.type.unwrapAll() as GraphQLObjectType | ||
|
||
// Recursively create type mapping | ||
val childTypeMappings = createTypeMappings( | ||
visitedVirtualTypes = visitedVirtualTypes, | ||
virtualType = virtualOutputType, | ||
backingType = backingOutputType, | ||
) | ||
|
||
listOf(VirtualTypeMapping(virtualOutputType, backingOutputType)) + childTypeMappings | ||
} else { | ||
emptyList() | ||
} | ||
} | ||
} | ||
|
||
data class VirtualTypeMapping( | ||
val virtualType: GraphQLObjectType, | ||
val backingType: GraphQLObjectType, | ||
) | ||
|
||
/** | ||
* Util to create [Map] | ||
*/ | ||
private fun List<VirtualTypeMapping>.virtualTypeToBackingTypeMap(): Map<String, String> { | ||
val mapping = mutableMapOf<String, String>() | ||
forEach { | ||
mapping[it.virtualType.name] = it.backingType.name | ||
} | ||
return mapping | ||
} | ||
|
||
/** | ||
* Util to create [Map] | ||
*/ | ||
private fun List<VirtualTypeMapping>.backingTypeToVirtualTypeMap(): Map<String, String> { | ||
val mapping = mutableMapOf<String, String>() | ||
forEach { | ||
mapping[it.backingType.name] = it.virtualType.name | ||
} | ||
return mapping | ||
} | ||
} | ||
|
||
/** | ||
* @return true if the element hasn't been visited before. | ||
* Also mutates the [MutableSet] to mark it as visited. | ||
*/ | ||
private fun MutableSet<String>.visit(element: String): Boolean { | ||
return if (contains(element)) { | ||
false | ||
} else { | ||
add(element) | ||
true | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
lib/src/main/java/graphql/nadel/engine/blueprint/NadelVirtualTypeContext.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package graphql.nadel.engine.blueprint | ||
|
||
import graphql.schema.GraphQLFieldDefinition | ||
import graphql.schema.GraphQLFieldsContainer | ||
|
||
data class NadelVirtualTypeContext( | ||
/** | ||
* The container of the virtual field that created this mapping. | ||
*/ | ||
val virtualFieldContainer: GraphQLFieldsContainer, | ||
/** | ||
* The virtual field that created this mapping. | ||
*/ | ||
val virtualField: GraphQLFieldDefinition, | ||
val virtualTypeToBackingType: Map<String, String>, | ||
val backingTypeToVirtualType: Map<String, String>, | ||
) |
Oops, something went wrong.