Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cosmos DB: Adds circular reference check for entities in graphQL schema #2192

Merged
merged 12 commits into from
May 3, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -193,24 +193,24 @@ private void ParseSchemaGraphQLFieldsForJoins()
/// <param name="tableCounter">Counter used to generate table alias</param>
/// <param name="parentEntity">indicates the parent entity for which we are processing the schema.
/// It is useful to get the JOIN statement information and create further new statements</param>
/// <param name="visitedEntity"> Keeps a track of the path in an entity, to detect circular reference</param>
/// <param name="visitedEntities"> Keeps a track of the path in an entity, to detect circular reference</param>
/// <remarks>It detects the circular reference in the schema while processing the schema and throws <seealso cref="DataApiBuilderException"/> </remarks>
private void ProcessSchema(
IReadOnlyList<FieldDefinitionNode> fields,
Dictionary<string, ObjectTypeDefinitionNode> schemaDocument,
string currentPath,
IncrementingInteger tableCounter,
EntityDbPolicyCosmosModel? parentEntity = null,
HashSet<string>? visitedEntity = 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> trackerForField = new();
if (visitedEntity is not null)
if (visitedEntities is not null)
{
trackerForField = visitedEntity;
trackerForField = visitedEntities;
sourabh1007 marked this conversation as resolved.
Show resolved Hide resolved
}

string entityType = field.Type.NamedType().Name.Value;
Expand Down Expand Up @@ -277,7 +277,7 @@ private void ParseSchemaGraphQLFieldsForJoins()
currentPath: isArrayType ? $"{alias}" : $"{currentPath}.{field.Name.Value}",
tableCounter: tableCounter,
parentEntity: isArrayType ? currentEntity : null,
visitedEntity: trackerForField);
visitedEntities: trackerForField);
}
}

Expand Down
9 changes: 4 additions & 5 deletions src/Service.Tests/Configuration/ConfigurationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2981,8 +2981,8 @@ public async Task TestEngineSupportViewsWithoutKeyFieldsInConfigForMsSQL()
}

/// <summary>
/// In CosmosDB NoSQL, we store data in the form of JSON.Practically, JSON can be very complex.
/// But DAB doesn't support JSON with circular references e.g if 'Character.Moon' is a valid JSON Path, the
/// In CosmosDB NoSQL, we store data in the form of JSON. Practically, JSON can be very complex.
/// But DAB doesn't support JSON with circular references e.g if 'Character.Moon' is a valid JSON Path, then
/// 'Moon.Character' should not be there, DAB would throw an exception during the load itself.
/// </summary>
/// <exception cref="ApplicationException"></exception>
Expand All @@ -3003,16 +3003,15 @@ public void ValidateGraphQLSchemaForCircularReference(string schema)
MockFileSystem fileSystem = new(new Dictionary<string, MockFileData>()
{
{ @"../schema.gql", new MockFileData(schema) },
{ FileSystemRuntimeConfigLoader.DEFAULT_CONFIG_FILE_NAME,
new MockFileData(baseConfig.ToJson()) }
{ DEFAULT_CONFIG_FILE_NAME, new MockFileData(baseConfig.ToJson()) }
});
FileSystemRuntimeConfigLoader loader = new(fileSystem);
RuntimeConfigProvider provider = new(loader);

DataApiBuilderException exception =
Assert.ThrowsException<DataApiBuilderException>(() => new CosmosSqlMetadataProvider(provider, fileSystem));
Assert.AreEqual("Circular reference detected in the provided GraphQL schema for entity 'Character'.", exception.Message);
Assert.AreEqual(System.Net.HttpStatusCode.InternalServerError, exception.StatusCode);
Assert.AreEqual(HttpStatusCode.InternalServerError, exception.StatusCode);
Assert.AreEqual(DataApiBuilderException.SubStatusCodes.ErrorInInitialization, exception.SubStatusCode);
}

Expand Down