Skip to content

Commit 9a90ca1

Browse files
Enable or Disable Multiple Create operation based on feature flag value (#2116)
## Why make this change? - Closes #1951 - PR #1983, #2103 add CLI options to enable or disable multiple mutation/multiple create operation through CLI. With the changes introduced in the mentioned PRs, the configuration properties successfully gets written to the config file. Also, during deserialization, the properties are read and the `MultipleMutationOptions`, `MultipleCreateOptions`, `GraphQLRuntimeOptions` objects are created accordingly. - The above-mentioned PRs do not introduce any change in DAB engine behavior depending on the configuration property values. - This PR introduces changes to read these fields and enable/disable multiple create operation depending on whether the feature is enabled/disabled through the config file. This is achieved by introducing behavior changes in the schema generation. ## What is this change? - This PR builds on top of a) Schema generation PR #1902 b) Rename nested-create to multiple create PR #2103 - When multiple create operation is disabled, > i) Fields belonging to the related entities are not created in the input object type are not created. > ii) Many type multiple create mutation nodes (ex: `createbooks`, `createpeople_multiple` ) are not created. > iii) ReferencingField directive is not applied on relationship fields, so they continue to remain required fields for the create mutation operation. > iv) Entities for linking objects are not created as they are relevant only in the context of multiple create operations. ## How was this tested? - [x] Unit Tests and Integration Tests - [x] Manual Tests **Note:** At the moment, multiple create operation is disabled in the config file generated for integration tests. This is because of the plan to merge in the Schema generation, AuthZ/N branches separately to the main branch. With just these 2 PRs, a multiple create operation will fail, hence, the disabling multiple create operation. At the moment, tests that perform validations specific to multiple create feature enable it by i) updating the runtime object (or) ii) creating a custom config in which the operation is enabled. ## Sample Request(s) ### When Multiple Create operation is enabled - MsSQL #### Related entity fields are created in the input object type ![image](https://github.com/Azure/data-api-builder/assets/11196553/7a3a8bbe-2742-43e0-98d7-9412ed05db33) #### Multiple type create operation is created in addition to point create operation ![image](https://github.com/Azure/data-api-builder/assets/11196553/c6513d9a-5b49-44cc-8fcc-1ed1f44f5f58) #### Querying related entities continue to work successfully ![image](https://github.com/Azure/data-api-builder/assets/11196553/4c1a61b8-0cbb-4a1e-afaa-1849d710be27) ### When Multiple Create operation is disabled - MsSQL #### Only fields belonging to the given entity are created in the input object type ![image](https://github.com/Azure/data-api-builder/assets/11196553/a3b6beb2-7245-4345-ba13-29d8905d859e) #### Multiple type create operation is not created ### When Multiple Create operation is enabled - Other relational database types #### Only fields belonging to the given entity are created in the input object type ![image](https://github.com/Azure/data-api-builder/assets/11196553/b2a4f7f6-b121-410d-806d-8c5772253080) #### Multiple type create operation is not created --------- Co-authored-by: Ayush Agarwal <[email protected]>
1 parent 9233c41 commit 9a90ca1

File tree

12 files changed

+532
-89
lines changed

12 files changed

+532
-89
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
namespace Azure.DataApiBuilder.Config.ObjectModel
5+
{
6+
public enum MultipleCreateSupportingDatabaseType
7+
{
8+
MSSQL
9+
}
10+
}

src/Config/ObjectModel/RuntimeConfig.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,4 +453,22 @@ public static bool IsHotReloadable()
453453
// always return false while hot reload is not an available feature.
454454
return false;
455455
}
456+
457+
/// <summary>
458+
/// Helper method to check if multiple create option is supported and enabled.
459+
///
460+
/// Returns true when
461+
/// 1. Multiple create operation is supported by the database type and
462+
/// 2. Multiple create operation is enabled in the runtime config.
463+
///
464+
/// </summary>
465+
public bool IsMultipleCreateOperationEnabled()
466+
{
467+
return Enum.GetNames(typeof(MultipleCreateSupportingDatabaseType)).Any(x => x.Equals(DataSource.DatabaseType.ToString(), StringComparison.OrdinalIgnoreCase)) &&
468+
(Runtime is not null &&
469+
Runtime.GraphQL is not null &&
470+
Runtime.GraphQL.MultipleMutationOptions is not null &&
471+
Runtime.GraphQL.MultipleMutationOptions.MultipleCreateOptions is not null &&
472+
Runtime.GraphQL.MultipleMutationOptions.MultipleCreateOptions.Enabled);
473+
}
456474
}

src/Core/Services/GraphQLSchemaCreator.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public class GraphQLSchemaCreator
4242
private readonly RuntimeEntities _entities;
4343
private readonly IAuthorizationResolver _authorizationResolver;
4444
private readonly RuntimeConfigProvider _runtimeConfigProvider;
45+
private bool _isMultipleCreateOperationEnabled;
4546

4647
/// <summary>
4748
/// Initializes a new instance of the <see cref="GraphQLSchemaCreator"/> class.
@@ -60,6 +61,7 @@ public GraphQLSchemaCreator(
6061
{
6162
RuntimeConfig runtimeConfig = runtimeConfigProvider.GetConfig();
6263

64+
_isMultipleCreateOperationEnabled = runtimeConfig.IsMultipleCreateOperationEnabled();
6365
_entities = runtimeConfig.Entities;
6466
_queryEngineFactory = queryEngineFactory;
6567
_mutationEngineFactory = mutationEngineFactory;
@@ -137,7 +139,7 @@ private ISchemaBuilder Parse(
137139
DocumentNode queryNode = QueryBuilder.Build(root, entityToDatabaseType, _entities, inputTypes, _authorizationResolver.EntityPermissionsMap, entityToDbObjects);
138140

139141
// Generate the GraphQL mutations from the provided objects
140-
DocumentNode mutationNode = MutationBuilder.Build(root, entityToDatabaseType, _entities, _authorizationResolver.EntityPermissionsMap, entityToDbObjects);
142+
DocumentNode mutationNode = MutationBuilder.Build(root, entityToDatabaseType, _entities, _authorizationResolver.EntityPermissionsMap, entityToDbObjects, _isMultipleCreateOperationEnabled);
141143

142144
return (queryNode, mutationNode);
143145
}
@@ -215,8 +217,7 @@ private DocumentNode GenerateSqlGraphQLObjects(RuntimeEntities entities, Diction
215217
configEntity: entity,
216218
entities: entities,
217219
rolesAllowedForEntity: rolesAllowedForEntity,
218-
rolesAllowedForFields: rolesAllowedForFields
219-
);
220+
rolesAllowedForFields: rolesAllowedForFields);
220221

221222
if (databaseObject.SourceType is not EntitySourceType.StoredProcedure)
222223
{
@@ -234,8 +235,13 @@ private DocumentNode GenerateSqlGraphQLObjects(RuntimeEntities entities, Diction
234235
}
235236
}
236237

237-
// For all the fields in the object which hold a foreign key reference to any referenced entity, add a foreign key directive.
238-
AddReferencingFieldDirective(entities, objectTypes);
238+
// ReferencingFieldDirective is added to eventually mark the referencing fields in the input object types as optional. When multiple create operations are disabled
239+
// the referencing fields should be required fields. Hence, ReferencingFieldDirective is added only when the multiple create operations are enabled.
240+
if (_isMultipleCreateOperationEnabled)
241+
{
242+
// For all the fields in the object which hold a foreign key reference to any referenced entity, add a foreign key directive.
243+
AddReferencingFieldDirective(entities, objectTypes);
244+
}
239245

240246
// Pass two - Add the arguments to the many-to-* relationship fields
241247
foreach ((string entityName, ObjectTypeDefinitionNode node) in objectTypes)
@@ -245,8 +251,13 @@ private DocumentNode GenerateSqlGraphQLObjects(RuntimeEntities entities, Diction
245251

246252
// Create ObjectTypeDefinitionNode for linking entities. These object definitions are not exposed in the schema
247253
// but are used to generate the object definitions of directional linking entities for (source, target) and (target, source) entities.
248-
Dictionary<string, ObjectTypeDefinitionNode> linkingObjectTypes = GenerateObjectDefinitionsForLinkingEntities();
249-
GenerateSourceTargetLinkingObjectDefinitions(objectTypes, linkingObjectTypes);
254+
// However, ObjectTypeDefinitionNode for linking entities are need only for multiple create operation. So, creating these only when multiple create operations are
255+
// enabled.
256+
if (_isMultipleCreateOperationEnabled)
257+
{
258+
Dictionary<string, ObjectTypeDefinitionNode> linkingObjectTypes = GenerateObjectDefinitionsForLinkingEntities();
259+
GenerateSourceTargetLinkingObjectDefinitions(objectTypes, linkingObjectTypes);
260+
}
250261

251262
// Return a list of all the object types to be exposed in the schema.
252263
Dictionary<string, FieldDefinitionNode> fields = new();

src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ namespace Azure.DataApiBuilder.Core.Services
2929
public class MsSqlMetadataProvider :
3030
SqlMetadataProvider<SqlConnection, SqlDataAdapter, SqlCommand>
3131
{
32+
private RuntimeConfigProvider _runtimeConfigProvider;
33+
3234
public MsSqlMetadataProvider(
3335
RuntimeConfigProvider runtimeConfigProvider,
3436
IAbstractQueryManagerFactory queryManagerFactory,
@@ -37,6 +39,7 @@ public MsSqlMetadataProvider(
3739
bool isValidateOnly = false)
3840
: base(runtimeConfigProvider, queryManagerFactory, logger, dataSourceName, isValidateOnly)
3941
{
42+
_runtimeConfigProvider = runtimeConfigProvider;
4043
}
4144

4245
public override string GetDefaultSchemaName()
@@ -219,7 +222,7 @@ protected override void PopulateMetadataForLinkingObject(
219222
string linkingObject,
220223
Dictionary<string, DatabaseObject> sourceObjects)
221224
{
222-
if (!GraphQLUtils.DoesRelationalDBSupportMultipleCreate(GetDatabaseType()))
225+
if (!_runtimeConfigProvider.GetConfig().IsMultipleCreateOperationEnabled())
223226
{
224227
// Currently we have this same class instantiated for both MsSql and DwSql.
225228
// This is a refactor we need to take care of in future.

src/Core/Services/MetadataProviders/SqlMetadataProvider.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -755,16 +755,22 @@ private void ProcessRelationships(
755755
referencedColumns: relationship.TargetFields,
756756
relationshipData);
757757

758-
// When a linking object is encountered for a database table, we will create a linking entity for the object.
759-
// Subsequently, we will also populate the Database object for the linking entity. This is used to infer
760-
// metadata about linking object needed to create GQL schema for multiple insertions.
761-
if (entity.Source.Type is EntitySourceType.Table)
758+
RuntimeConfig runtimeConfig = _runtimeConfigProvider.GetConfig();
759+
760+
// Populating metadata for linking object is only required when multiple create operation is enabled and those database types that support multiple create operation.
761+
if (runtimeConfig.IsMultipleCreateOperationEnabled())
762762
{
763-
PopulateMetadataForLinkingObject(
764-
entityName: entityName,
765-
targetEntityName: targetEntityName,
766-
linkingObject: relationship.LinkingObject,
767-
sourceObjects: sourceObjects);
763+
// When a linking object is encountered for a database table, we will create a linking entity for the object.
764+
// Subsequently, we will also populate the Database object for the linking entity. This is used to infer
765+
// metadata about linking object needed to create GQL schema for multiple insertions.
766+
if (entity.Source.Type is EntitySourceType.Table)
767+
{
768+
PopulateMetadataForLinkingObject(
769+
entityName: entityName,
770+
targetEntityName: targetEntityName,
771+
linkingObject: relationship.LinkingObject,
772+
sourceObjects: sourceObjects);
773+
}
768774
}
769775
}
770776
else if (relationship.Cardinality == Cardinality.One)

src/Service.GraphQLBuilder/GraphQLUtils.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ public static class GraphQLUtils
3232
// Delimiter used to separate linking entity prefix/source entity name/target entity name, in the name of a linking entity.
3333
private const string ENTITY_NAME_DELIMITER = "$";
3434

35-
public static HashSet<DatabaseType> RELATIONAL_DBS_SUPPORTING_MULTIPLE_CREATE = new() { DatabaseType.MSSQL };
36-
3735
public static HashSet<DatabaseType> RELATIONAL_DBS = new() { DatabaseType.MSSQL, DatabaseType.MySQL,
3836
DatabaseType.DWSQL, DatabaseType.PostgreSQL, DatabaseType.CosmosDB_PostgreSQL };
3937

@@ -72,14 +70,6 @@ public static bool IsBuiltInType(ITypeNode typeNode)
7270
return builtInTypes.Contains(name);
7371
}
7472

75-
/// <summary>
76-
/// Helper method to evaluate whether DAB supports multiple create for a particular database type.
77-
/// </summary>
78-
public static bool DoesRelationalDBSupportMultipleCreate(DatabaseType databaseType)
79-
{
80-
return RELATIONAL_DBS_SUPPORTING_MULTIPLE_CREATE.Contains(databaseType);
81-
}
82-
8373
/// <summary>
8474
/// Helper method to evaluate whether database type represents a NoSQL database.
8575
/// </summary>

0 commit comments

Comments
 (0)