in src/Service.GraphQLBuilder/Sql/SchemaConverter.cs [151:229]
private static ObjectTypeDefinitionNode CreateObjectTypeDefinitionForTableOrView(
string entityName,
DatabaseObject databaseObject,
Entity configEntity,
RuntimeEntities entities,
IEnumerable<string> rolesAllowedForEntity,
IDictionary<string, IEnumerable<string>> rolesAllowedForFields)
{
Dictionary<string, FieldDefinitionNode> fieldDefinitionNodes = new();
SourceDefinition sourceDefinition = databaseObject.SourceDefinition;
foreach ((string columnName, ColumnDefinition column) in sourceDefinition.Columns)
{
List<DirectiveNode> directives = new();
if (sourceDefinition.PrimaryKey.Contains(columnName))
{
directives.Add(new DirectiveNode(PrimaryKeyDirectiveType.DirectiveName, new ArgumentNode("databaseType", column.SystemType.Name)));
}
if (column.IsReadOnly)
{
directives.Add(new DirectiveNode(AutoGeneratedDirectiveType.DirectiveName));
}
if (column.DefaultValue is not null)
{
IValueNode arg = CreateValueNodeFromDbObjectMetadata(column.DefaultValue);
directives.Add(new DirectiveNode(DefaultValueDirectiveType.DirectiveName, new ArgumentNode("value", arg)));
}
// A field is added to the ObjectTypeDefinition when:
// 1. The entity is a linking entity. A linking entity is not exposed by DAB for query/mutation but the fields are required to generate
// object definitions of directional linking entities from source to target.
// 2. The entity is not a linking entity and there is atleast one role allowed to access the field.
if (rolesAllowedForFields.TryGetValue(key: columnName, out IEnumerable<string>? roles) || configEntity.IsLinkingEntity)
{
// Roles will not be null here if TryGetValue evaluates to true, so here we check if there are any roles to process.
// This check is bypassed for linking entities for the same reason explained above.
if (configEntity.IsLinkingEntity || roles is not null && roles.Count() > 0)
{
FieldDefinitionNode field = GenerateFieldForColumn(configEntity, columnName, column, directives, roles);
fieldDefinitionNodes.Add(columnName, field);
}
}
}
// A linking entity is not exposed in the runtime config file but is used by DAB to support multiple mutations on entities with M:N relationship.
// Hence we don't need to process relationships for the linking entity itself.
if (!configEntity.IsLinkingEntity)
{
// For an entity exposed in the config, process the relationships (if there are any)
// sequentially and generate fields for them - to be added to the entity's ObjectTypeDefinition at the end.
if (configEntity.Relationships is not null)
{
foreach ((string relationshipName, EntityRelationship relationship) in configEntity.Relationships)
{
FieldDefinitionNode relationshipField = GenerateFieldForRelationship(
entityName,
databaseObject,
entities,
relationshipName,
relationship);
fieldDefinitionNodes.Add(relationshipField.Name.Value, relationshipField);
}
}
}
// Top-level object type definition name should be singular.
// The singularPlural.Singular value is used, and if not configured,
// the top-level entity name value is used. No singularization occurs
// if the top-level entity name is already plural.
return new ObjectTypeDefinitionNode(
location: null,
name: new(value: GetDefinedSingularName(entityName, configEntity)),
description: null,
directives: GenerateObjectTypeDirectivesForEntity(entityName, configEntity, rolesAllowedForEntity),
new List<NamedTypeNode>(),
fieldDefinitionNodes.Values.ToImmutableList());
}