private static string DetermineReferencingEntityBasedOnRequestBody()

in src/Core/Resolvers/MultipleCreateOrderHelper.cs [197:326]


        private static string DetermineReferencingEntityBasedOnRequestBody(
            string relationshipName,
            string sourceEntityName,
            string targetEntityName,
            DatabaseObject sourceDbObject,
            DatabaseObject targetDbObject,
            Dictionary<string, IValueNode?> columnDataInSourceBody,
            Dictionary<string, IValueNode?> columnDataInTargetBody,
            int nestingLevel)
        {
            RelationshipFields relationshipFields = GetRelationshipFieldsInSourceAndTarget(
                    sourceEntityName: sourceEntityName,
                    targetEntityName: targetEntityName,
                    sourceDbObject: sourceDbObject);

            List<string> sourceFields = relationshipFields.SourceFields;
            List<string> targetFields = relationshipFields.TargetFields;

            // Collect column metadata for source/target columns.
            Dictionary<string, ColumnDefinition> sourceColumnDefinitions = sourceDbObject.SourceDefinition.Columns;
            Dictionary<string, ColumnDefinition> targetColumnDefinitions = targetDbObject.SourceDefinition.Columns;

            bool doesSourceContainAnyAutogenRelationshipField = false;
            bool doesTargetContainAnyAutogenRelationshipField = false;
            bool doesSourceBodyContainAnyRelationshipField = false;
            bool doesTargetBodyContainAnyRelationshipField = false;

            // Set to false when source body can't assume a non-null value for one or more relationship fields.
            // For the current relationship column to process, the value can be assumed when:
            // 1.The relationship column is autogenerated, or
            // 2.The request body provides a value for the relationship column.
            bool canSourceAssumeAllRelationshipFieldValues = true;

            // Set to false when target body can't assume a non-null value for one or more relationship fields.
            bool canTargetAssumeAllRelationshipFieldsValues = true;

            // Loop over all the relationship fields in source/target to appropriately set the above variables.
            for (int idx = 0; idx < sourceFields.Count; idx++)
            {
                string relationshipFieldInSource = sourceFields[idx];
                string relationshipFieldInTarget = targetFields[idx];

                // Determine whether the source/target relationship fields for this pair are autogenerated.
                bool isSourceRelationshipFieldAutogenerated = sourceColumnDefinitions[relationshipFieldInSource].IsAutoGenerated;
                bool isTargetRelationshipFieldAutogenerated = targetColumnDefinitions[relationshipFieldInTarget].IsAutoGenerated;

                // Update whether source/target contains any relationship field which is autogenerated.
                doesSourceContainAnyAutogenRelationshipField = doesSourceContainAnyAutogenRelationshipField || isSourceRelationshipFieldAutogenerated;
                doesTargetContainAnyAutogenRelationshipField = doesTargetContainAnyAutogenRelationshipField || isTargetRelationshipFieldAutogenerated;

                // When both source/target entities contain an autogenerated relationship field, 
                // DAB can't determine the referencing entity. That's because a referencing entity's
                // referencing fields are derived from the insertion of the referenced entity but
                // we cannot assign a derived value to an autogenerated field.
                if (doesSourceContainAnyAutogenRelationshipField && doesTargetContainAnyAutogenRelationshipField)
                {
                    throw new DataApiBuilderException(
                        message: $"Cannot execute multiple-create because both the source entity: {sourceEntityName} and the target entity: " +
                        $"{targetEntityName} contain autogenerated fields for relationship: {relationshipName} at level: {nestingLevel}",
                        statusCode: HttpStatusCode.BadRequest,
                        subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
                }

                // Determine whether the input data for source/target contain a value (could be null) for this pair of relationship fields.
                bool doesSourceBodyContainThisRelationshipField = columnDataInSourceBody.TryGetValue(relationshipFieldInSource, out IValueNode? sourceColumnvalue);
                bool doesTargetBodyContainThisRelationshipField = columnDataInTargetBody.TryGetValue(relationshipFieldInTarget, out IValueNode? targetColumnvalue);

                // Update whether input data for source/target contains any relationship field.
                doesSourceBodyContainAnyRelationshipField = doesSourceBodyContainAnyRelationshipField || doesSourceBodyContainThisRelationshipField;
                doesTargetBodyContainAnyRelationshipField = doesTargetBodyContainAnyRelationshipField || doesTargetBodyContainThisRelationshipField;

                // If the source entity contains a relationship field in request body which suggests we perform the insertion first in source entity,
                // and there is an autogenerated relationship field in the target entity which suggests we perform the insertion first in target entity,
                // we cannot determine a valid order of insertion.
                if (doesSourceBodyContainAnyRelationshipField && doesTargetContainAnyAutogenRelationshipField)
                {
                    throw new DataApiBuilderException(
                        message: $"Input for source entity: {sourceEntityName} for the relationship: {relationshipName} at level: {nestingLevel} cannot contain the field: {relationshipFieldInSource}.",
                        statusCode: HttpStatusCode.BadRequest,
                        subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
                }

                // If the target entity contains a relationship field in request body which suggests we perform the insertion first in target entity,
                // and there is an autogenerated relationship field in the source entity which suggests we perform the insertion first in source entity,
                // we cannot determine a valid order of insertion.
                if (doesTargetBodyContainAnyRelationshipField && doesSourceContainAnyAutogenRelationshipField)
                {
                    throw new DataApiBuilderException(
                        message: $"Input for target entity: {targetEntityName} for the relationship: {relationshipName} at level: {nestingLevel} cannot contain the field: {relationshipFieldInSource}.",
                        statusCode: HttpStatusCode.BadRequest,
                        subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
                }

                // If relationship columns are present in the input for both the source and target entities,
                // we cannot choose one entity as the referencing entity. This is because for a referencing entity,
                // the values for all the referencing fields should be derived from the insertion in the referenced entity.
                // However, here both entities contain atleast one relationship field whose value is provided in the request.
                if (doesSourceBodyContainAnyRelationshipField && doesTargetBodyContainAnyRelationshipField)
                {
                    throw new DataApiBuilderException(
                        message: $"The relationship fields must be specified for either the source entity: {sourceEntityName} or the target entity: {targetEntityName} " +
                        $"for the relationship: {relationshipName} at level: {nestingLevel}.",
                        statusCode: HttpStatusCode.BadRequest,
                        subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
                }

                // The source/target entities can assume a value for insertion for a relationship field if:
                // 1. The field is autogenerated, or
                // 2. The field is given a non-null value by the user - since we don't allow null values for a relationship field.
                bool canSourceAssumeThisFieldValue = isSourceRelationshipFieldAutogenerated || sourceColumnvalue is not null;
                bool canTargetAssumeThisFieldValue = isTargetRelationshipFieldAutogenerated || targetColumnvalue is not null;

                // Update whether all the values(non-null) for relationship fields are available for source/target.
                canSourceAssumeAllRelationshipFieldValues = canSourceAssumeAllRelationshipFieldValues && canSourceAssumeThisFieldValue;
                canTargetAssumeAllRelationshipFieldsValues = canTargetAssumeAllRelationshipFieldsValues && canTargetAssumeThisFieldValue;

                // If the values for all relationship fields cannot be assumed for neither source nor target,
                // the multiple create request cannot be executed.
                if (!canSourceAssumeAllRelationshipFieldValues && !canTargetAssumeAllRelationshipFieldsValues)
                {
                    throw new DataApiBuilderException(
                        message: $"Neither source entity: {sourceEntityName} nor the target entity: {targetEntityName} for the relationship: {relationshipName} at level: {nestingLevel} " +
                        $"provide sufficient data to perform a multiple-create (related insertion) on the entities.",
                        statusCode: HttpStatusCode.BadRequest,
                        subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
                }
            }

            return canSourceAssumeAllRelationshipFieldValues ? targetEntityName : sourceEntityName;
        }