public void ValidateUpsertRequestContext()

in src/Core/Services/RequestValidator.cs [351:425]


        public void ValidateUpsertRequestContext(UpsertRequestContext upsertRequestCtx)
        {
            ISqlMetadataProvider sqlMetadataProvider = GetSqlMetadataProvider(upsertRequestCtx.EntityName);
            IEnumerable<string> fieldsInRequestBody = upsertRequestCtx.FieldValuePairsInBody.Keys;
            bool isRequestBodyStrict = IsRequestBodyStrict();
            SourceDefinition sourceDefinition = TryGetSourceDefinition(upsertRequestCtx.EntityName, sqlMetadataProvider);

            // Each field that is checked against the DB schema is removed
            // from the hash set of unvalidated fields.
            // At the end, if we end up with extraneous unvalidated fields, we throw error.
            HashSet<string> unValidatedFields = new(fieldsInRequestBody);

            foreach (KeyValuePair<string, ColumnDefinition> column in sourceDefinition.Columns)
            {
                // if column is not exposed we skip
                if (!sqlMetadataProvider.TryGetExposedColumnName(
                    entityName: upsertRequestCtx.EntityName,
                    backingFieldName: column.Key,
                    out string? exposedName))
                {
                    continue;
                }

                if (upsertRequestCtx.FieldValuePairsInBody.ContainsKey(exposedName!) && column.Value.IsReadOnly)
                {
                    if (isRequestBodyStrict)
                    {
                        throw new DataApiBuilderException(
                            message: $"Field '{exposedName}' cannot be included in the request body.",
                            statusCode: HttpStatusCode.BadRequest,
                            subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
                    }

                    unValidatedFields.Remove(exposedName!);
                }

                // Primary Key(s) should not be present in the request body. We do not fail a request
                // if a PK is autogenerated here, because an UPSERT request may only need to update a
                // record. If an insert occurs on a table with autogenerated primary key,
                // a database error will be returned.
                if (sourceDefinition.PrimaryKey.Contains(column.Key))
                {
                    continue;
                }

                // Request body must have value defined for included non-nullable columns
                if (!column.Value.IsNullable && fieldsInRequestBody.Contains(exposedName))
                {
                    if (upsertRequestCtx.FieldValuePairsInBody[exposedName!] is null)
                    {
                        throw new DataApiBuilderException(
                            message: $"Invalid value for field {exposedName} in request body.",
                            statusCode: HttpStatusCode.BadRequest,
                            subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
                    }
                }

                bool isReplacementUpdate = (upsertRequestCtx.OperationType == EntityActionOperation.Upsert) ? true : false;
                if (ValidateColumn(column.Value, exposedName!, fieldsInRequestBody, isReplacementUpdate, isRequestBodyStrict))
                {
                    unValidatedFields.Remove(exposedName!);
                }
            }

            // There may be unvalidated fields remaining because of extraneous fields in request body
            // which are not mapped to the table. We throw an exception only when we operate in strict mode,
            // i.e. when extraneous fields are not allowed.
            if (unValidatedFields.Any() && isRequestBodyStrict)
            {
                throw new DataApiBuilderException(
                    message: "Invalid request body. Either insufficient or extra fields supplied.",
                    statusCode: HttpStatusCode.BadRequest,
                    subStatusCode: DataApiBuilderException.SubStatusCodes.BadRequest);
            }
        }