in src/Core/Services/MetadataProviders/CosmosSqlMetadataProvider.cs [211:298]
private void ProcessSchema(
IReadOnlyList<FieldDefinitionNode> fields,
Dictionary<string, ObjectTypeDefinitionNode> schemaDocument,
string currentPath,
IncrementingInteger tableCounter,
EntityDbPolicyCosmosModel? parentEntity = null,
HashSet<string>? visitedEntities = null)
{
// Traverse the fields and add them to the path
foreach (FieldDefinitionNode field in fields)
{
// Create a tracker to keep track of visited entities to detect circular references
HashSet<string> trackerForFields = new();
if (visitedEntities is not null)
{
trackerForFields = visitedEntities;
}
// If the entity is build-in type, do not go further to check circular reference
if (GraphQLUtils.IsBuiltInType(field.Type))
{
continue;
}
string entityType = field.Type.NamedType().Name.Value;
AssertIfEntityIsAvailableInConfig(entityType);
// If the entity is already visited, then it is a circular reference
if (!trackerForFields.Add(entityType))
{
throw new DataApiBuilderException(
message: $"Circular reference detected in the provided GraphQL schema for entity '{entityType}'.",
statusCode: System.Net.HttpStatusCode.InternalServerError,
subStatusCode: DataApiBuilderException.SubStatusCodes.ErrorInInitialization);
}
string? alias = null;
bool isArrayType = field.Type is ListTypeNode;
if (isArrayType)
{
// Since we don't have query structure here,
// we are going to generate alias and use this counter to generate unique alias for each table at later stage.
alias = $"table{tableCounter.Next()}";
}
EntityDbPolicyCosmosModel currentEntity = new(
Path: currentPath,
EntityName: entityType,
ColumnName: field.Name.Value,
Alias: alias);
// If entity is defined in the runtime config, only then generate Join for this entity
if (EntityWithJoins.ContainsKey(entityType))
{
EntityWithJoins[entityType].Add(currentEntity);
}
else
{
EntityWithJoins.Add(
entityType,
new List<EntityDbPolicyCosmosModel>() {
currentEntity
});
}
if (parentEntity is not null)
{
if (string.IsNullOrEmpty(currentEntity.JoinStatement))
{
currentEntity.JoinStatement = parentEntity.JoinStatement;
}
else
{
currentEntity.JoinStatement = parentEntity.JoinStatement + " JOIN " + currentEntity.JoinStatement;
}
}
// If the field is an array type, we need to create a table alias which will be used when creating JOINs to that table.
ProcessSchema(
fields: schemaDocument[entityType].Fields,
schemaDocument: schemaDocument,
currentPath: isArrayType ? $"{alias}" : $"{currentPath}.{field.Name.Value}",
tableCounter: tableCounter,
parentEntity: isArrayType ? currentEntity : null,
visitedEntities: trackerForFields);
}
}