private void ProcessMultipleCreateInputField()

in src/Core/Resolvers/SqlMutationEngine.cs [1183:1372]


        private void ProcessMultipleCreateInputField(
            IMiddlewareContext context,
            object? unparsedInputFields,
            ISqlMetadataProvider sqlMetadataProvider,
            MultipleCreateStructure multipleCreateStructure,
            int nestingLevel)
        {

            if (multipleCreateStructure.InputMutParams is null || unparsedInputFields is null)
            {
                throw new DataApiBuilderException(
                        message: "The input for a multiple create mutation operation cannot be null.",
                        statusCode: HttpStatusCode.BadRequest,
                        subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
            }

            // For One - Many and Many - Many relationship types, processing logic is distinctly executed for each
            // object in the input list.
            // So, when the input parameters is of list type, we iterate over the list
            // and call the same method for each element.
            if (multipleCreateStructure.InputMutParams.GetType().GetGenericTypeDefinition() == typeof(List<>))
            {
                List<IDictionary<string, object?>> parsedInputItems = (List<IDictionary<string, object?>>)multipleCreateStructure.InputMutParams;
                List<IValueNode> unparsedInputFieldList = (List<IValueNode>)unparsedInputFields;
                int parsedInputItemIndex = 0;

                foreach (IDictionary<string, object?> parsedInputItem in parsedInputItems)
                {
                    MultipleCreateStructure multipleCreateStructureForCurrentItem = new(
                        entityName: multipleCreateStructure.EntityName,
                        parentEntityName: multipleCreateStructure.ParentEntityName,
                        inputMutParams: parsedInputItem,
                        isLinkingTableInsertionRequired: multipleCreateStructure.IsLinkingTableInsertionRequired)
                    {
                        CurrentEntityParams = multipleCreateStructure.CurrentEntityParams,
                        LinkingTableParams = multipleCreateStructure.LinkingTableParams
                    };

                    Dictionary<string, Dictionary<string, object?>> primaryKeysOfCreatedItems = new();
                    IValueNode? nodeForCurrentInput = unparsedInputFieldList[parsedInputItemIndex];
                    if (nodeForCurrentInput is null)
                    {
                        throw new DataApiBuilderException(
                            message: "Error when processing the mutation request",
                            statusCode: HttpStatusCode.BadRequest,
                            subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
                    }

                    ProcessMultipleCreateInputField(context, nodeForCurrentInput.Value, sqlMetadataProvider, multipleCreateStructureForCurrentItem, nestingLevel);
                    parsedInputItemIndex++;
                }
            }
            else
            {
                if (unparsedInputFields is not List<ObjectFieldNode> parameterNodes)
                {
                    throw new DataApiBuilderException(
                        message: "Error occurred while processing the mutation request",
                        statusCode: HttpStatusCode.BadRequest,
                        subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
                }

                string entityName = multipleCreateStructure.EntityName;
                Entity entity = _runtimeConfigProvider.GetConfig().Entities[entityName];

                // Classifiy the relationship fields (if present in the input request) into referencing and referenced relationships and
                // populate multipleCreateStructure.ReferencingRelationships and multipleCreateStructure.ReferencedRelationships respectively.
                DetermineReferencedAndReferencingRelationships(context, multipleCreateStructure, sqlMetadataProvider, entity.Relationships, parameterNodes);
                PopulateCurrentAndLinkingEntityParams(multipleCreateStructure, sqlMetadataProvider, entity.Relationships);

                SourceDefinition currentEntitySourceDefinition = sqlMetadataProvider.GetSourceDefinition(entityName);
                currentEntitySourceDefinition.SourceEntityRelationshipMap.TryGetValue(entityName, out RelationshipMetadata? currentEntityRelationshipMetadata);

                // Process referenced relationships
                foreach ((string relationshipName, object? relationshipFieldValue) in multipleCreateStructure.ReferencedRelationships)
                {
                    string relatedEntityName = GraphQLUtils.GetRelationshipTargetEntityName(entity, entityName, relationshipName);
                    MultipleCreateStructure referencedRelationshipMultipleCreateStructure = new(entityName: relatedEntityName, parentEntityName: entityName, inputMutParams: relationshipFieldValue);
                    IValueNode node = GraphQLUtils.GetFieldNodeForGivenFieldName(parameterNodes, relationshipName);
                    ProcessMultipleCreateInputField(context, node.Value, sqlMetadataProvider, referencedRelationshipMultipleCreateStructure, nestingLevel + 1);

                    if (sqlMetadataProvider.TryGetFKDefinition(
                                                    sourceEntityName: entityName,
                                                    targetEntityName: relatedEntityName,
                                                    referencingEntityName: entityName,
                                                    referencedEntityName: relatedEntityName,
                                                    out ForeignKeyDefinition? foreignKeyDefinition,
                                                    isMToNRelationship: false))
                    {
                        PopulateReferencingFields(
                            sqlMetadataProvider: sqlMetadataProvider,
                            multipleCreateStructure: multipleCreateStructure,
                            fkDefinition: foreignKeyDefinition,
                            computedRelationshipFields: referencedRelationshipMultipleCreateStructure.CurrentEntityCreatedValues,
                            isLinkingTable: false,
                            entityName: relatedEntityName);
                    }
                }

                multipleCreateStructure.CurrentEntityCreatedValues = BuildAndExecuteInsertDbQueries(
                                                                          sqlMetadataProvider: sqlMetadataProvider,
                                                                          entityName: entityName,
                                                                          parentEntityName: entityName,
                                                                          parameters: multipleCreateStructure.CurrentEntityParams!,
                                                                          sourceDefinition: currentEntitySourceDefinition,
                                                                          isLinkingEntity: false,
                                                                          nestingLevel: nestingLevel);

                //Perform an insertion in the linking table if required
                if (multipleCreateStructure.IsLinkingTableInsertionRequired)
                {
                    if (multipleCreateStructure.LinkingTableParams is null)
                    {
                        multipleCreateStructure.LinkingTableParams = new Dictionary<string, object?>();
                    }

                    // Consider the mutation request:
                    // mutation{
                    //     createbook(item: {
                    //         title: "Book Title",
                    //         publisher_id: 1234,
                    //         authors: [
                    //             {...} ,
                    //             {...}
                    //         ]
                    //      }) {
                    //          ...
                    //      }
                    // There exists two relationships for a linking table.
                    // 1. Relationship between the parent entity (Book) and the linking table.
                    // 2. Relationship between the current entity (Author) and the linking table.
                    // To construct the insert database query for the linking table, relationship fields from both the
                    // relationships are required. 

                    // Populate Current entity's relationship fields
                    List<ForeignKeyDefinition> foreignKeyDefinitions = currentEntityRelationshipMetadata!.TargetEntityToFkDefinitionMap[multipleCreateStructure.ParentEntityName];
                    ForeignKeyDefinition fkDefinition = foreignKeyDefinitions[0];
                    PopulateReferencingFields(sqlMetadataProvider, multipleCreateStructure, fkDefinition, multipleCreateStructure.CurrentEntityCreatedValues, isLinkingTable: true);

                    string linkingEntityName = GraphQLUtils.GenerateLinkingEntityName(multipleCreateStructure.ParentEntityName, entityName);
                    SourceDefinition linkingTableSourceDefinition = sqlMetadataProvider.GetSourceDefinition(linkingEntityName);

                    _ = BuildAndExecuteInsertDbQueries(
                            sqlMetadataProvider: sqlMetadataProvider,
                            entityName: linkingEntityName,
                            parentEntityName: entityName,
                            parameters: multipleCreateStructure.LinkingTableParams!,
                            sourceDefinition: linkingTableSourceDefinition,
                            isLinkingEntity: true,
                            nestingLevel: nestingLevel);
                }

                // Process referencing relationships
                foreach ((string relationshipFieldName, object? relationshipFieldValue) in multipleCreateStructure.ReferencingRelationships)
                {
                    string relatedEntityName = GraphQLUtils.GetRelationshipTargetEntityName(entity, entityName, relationshipFieldName);
                    MultipleCreateStructure referencingRelationshipMultipleCreateStructure = new(entityName: relatedEntityName,
                                                                                                 parentEntityName: entityName,
                                                                                                 inputMutParams: relationshipFieldValue,
                                                                                                 isLinkingTableInsertionRequired: GraphQLUtils.IsMToNRelationship(entity, relationshipFieldName));
                    IValueNode node = GraphQLUtils.GetFieldNodeForGivenFieldName(parameterNodes, relationshipFieldName);

                    // Many-Many relationships are marked as Referencing relationships
                    // because the linking table insertion can happen only
                    // when records have been successfully created in both the entities involved in the relationship.
                    // The entities involved do not derive any fields from each other. Only the linking table derives the
                    // primary key fields from the entities involved in the relationship.
                    // For a M:N relationships, the referencing fields are populated in LinkingTableParams whereas for  
                    // a 1:N relationship, referencing fields will be populated in CurrentEntityParams.
                    if (sqlMetadataProvider.TryGetFKDefinition(
                            sourceEntityName: entityName,
                            targetEntityName: relatedEntityName,
                            referencingEntityName: relatedEntityName,
                            referencedEntityName: entityName,
                            out ForeignKeyDefinition? referencingEntityFKDefinition,
                            isMToNRelationship: referencingRelationshipMultipleCreateStructure.IsLinkingTableInsertionRequired))
                    {
                        PopulateReferencingFields(
                            sqlMetadataProvider: sqlMetadataProvider,
                            multipleCreateStructure: referencingRelationshipMultipleCreateStructure,
                            fkDefinition: referencingEntityFKDefinition,
                            computedRelationshipFields: multipleCreateStructure.CurrentEntityCreatedValues,
                            isLinkingTable: referencingRelationshipMultipleCreateStructure.IsLinkingTableInsertionRequired,
                            entityName: entityName);
                    }

                    ProcessMultipleCreateInputField(context, node.Value, sqlMetadataProvider, referencingRelationshipMultipleCreateStructure, nestingLevel + 1);
                }
            }
        }