in src/Service.GraphQLBuilder/Mutations/CreateMutationBuilder.cs [35:160]
private static InputObjectTypeDefinitionNode GenerateCreateInputTypeForRelationalDb(
Dictionary<NameNode, InputObjectTypeDefinitionNode> inputs,
ObjectTypeDefinitionNode objectTypeDefinitionNode,
string entityName,
NameNode name,
NameNode baseEntityName,
IEnumerable<HotChocolate.Language.IHasName> definitions,
DatabaseType databaseType,
RuntimeEntities entities,
bool IsMultipleCreateOperationEnabled)
{
NameNode inputName = GenerateInputTypeName(name.Value);
if (inputs.ContainsKey(inputName))
{
return inputs[inputName];
}
// The input fields for a create object will be a combination of:
// 1. Scalar input fields corresponding to columns which belong to the table.
// 2. Complex input fields corresponding to related (target) entities (table backed entities, for now)
// which are defined in the runtime config.
List<InputValueDefinitionNode> inputFields = new();
// 1. Scalar input fields.
IEnumerable<InputValueDefinitionNode> scalarInputFields = objectTypeDefinitionNode.Fields
.Where(field => IsBuiltInType(field.Type) && !IsAutoGeneratedField(field))
.Select(field =>
{
return GenerateScalarInputType(name, field, IsMultipleCreateOperationEnabled);
});
// Add scalar input fields to list of input fields for current input type.
inputFields.AddRange(scalarInputFields);
// Create input object for this entity.
InputObjectTypeDefinitionNode input =
new(
location: null,
inputName,
new StringValueNode($"Input type for creating {name}"),
new List<DirectiveNode>(),
inputFields
);
// Add input object to the dictionary of entities for which input object has already been created.
// This input object currently holds only scalar fields.
// The complex fields (for related entities) would be added later when we return from recursion.
// Adding the input object to the dictionary ensures that we don't go into infinite recursion and return whenever
// we find that the input object has already been created for the entity.
inputs.Add(input.Name, input);
// Generate fields for related entities when
// 1. Multiple mutation operations are supported for the database type.
// 2. Multiple mutation operations are enabled.
if (IsMultipleCreateOperationEnabled)
{
// 2. Complex input fields.
// Evaluate input objects for related entities.
IEnumerable<InputValueDefinitionNode> complexInputFields =
objectTypeDefinitionNode.Fields
.Where(field => !IsBuiltInType(field.Type) && IsComplexFieldAllowedForCreateInputInRelationalDb(field, definitions))
.Select(field =>
{
string typeName = RelationshipDirectiveType.Target(field);
HotChocolate.Language.IHasName? def = definitions.FirstOrDefault(d => d.Name.Value.Equals(typeName));
if (def is null)
{
throw new DataApiBuilderException(
message: $"The type {typeName} is not a known GraphQL type, and cannot be used in this schema.",
statusCode: HttpStatusCode.ServiceUnavailable,
subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization);
}
if (!entities.TryGetValue(entityName, out Entity? entity) || entity.Relationships is null)
{
throw new DataApiBuilderException(
message: $"Could not find entity metadata for entity: {entityName}.",
statusCode: HttpStatusCode.ServiceUnavailable,
subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization);
}
string targetEntityName = entity.Relationships[field.Name.Value].TargetEntity;
if (IsMToNRelationship(entity, field.Name.Value))
{
// The field can represent a related entity with M:N relationship with the parent.
NameNode baseObjectTypeNameForField = new(typeName);
typeName = GenerateLinkingNodeName(baseEntityName.Value, typeName);
def = (ObjectTypeDefinitionNode)definitions.FirstOrDefault(d => d.Name.Value.Equals(typeName))!;
// Get entity definition for this ObjectTypeDefinitionNode.
// Recurse for evaluating input objects for related entities.
return GenerateComplexInputTypeForRelationalDb(
entityName: targetEntityName,
inputs: inputs,
definitions: definitions,
field: field,
typeName: typeName,
targetObjectTypeName: baseObjectTypeNameForField,
objectTypeDefinitionNode: (ObjectTypeDefinitionNode)def,
databaseType: databaseType,
entities: entities,
IsMultipleCreateOperationEnabled: IsMultipleCreateOperationEnabled);
}
// Get entity definition for this ObjectTypeDefinitionNode.
// Recurse for evaluating input objects for related entities.
return GenerateComplexInputTypeForRelationalDb(
entityName: targetEntityName,
inputs: inputs,
definitions: definitions,
field: field,
typeName: typeName,
targetObjectTypeName: new(typeName),
objectTypeDefinitionNode: (ObjectTypeDefinitionNode)def,
databaseType: databaseType,
entities: entities,
IsMultipleCreateOperationEnabled: IsMultipleCreateOperationEnabled);
});
// Append relationship fields to the input fields.
inputFields.AddRange(complexInputFields);
}
return input;
}