OData / odata.net

ODataLib: Open Data Protocol - .NET Libraries and Frameworks
https://docs.microsoft.com/odata
Other
686 stars 349 forks source link

ODL library allows you to write enum values onto properties of other primitive types #3054

Open gathogojr opened 2 weeks ago

gathogojr commented 2 weeks ago

ODL library allows you to write enum values onto properties of other primitive types

Assemblies affected

Reproduce steps

Given the following data models:

public class Order
{
    public int Id { get; set; }
    public decimal Amount { get; set; }
    public DateTime OrderDate { get; set; }
}

public enum Color
{
    Black,
    White
}

The following code for writing an OData payload executes okay but produces an invalid output (included in the snippet as a comment):

var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Order>("Orders");
modelBuilder.EnumType<Color>();

var model = modelBuilder.GetEdmModel();

var orderEntityType = model.SchemaElements.First(d => d.Name == "Order") as IEdmEntityType;
var ordersEntitySet = model.EntityContainer.FindEntitySet("Orders");

var messageWriterSettings = new ODataMessageWriterSettings
{
    EnableMessageStreamDisposal = false,
    Version = ODataVersion.V4,
    ODataUri = new ODataUri { ServiceRoot = new Uri("http://tempuri.org") }
};

var tempStream = new MemoryStream();

IODataResponseMessage asyncResponseMessage = new InMemoryMessage { Stream = tempStream };

await using (var messageWriter = new ODataMessageWriter(asyncResponseMessage, messageWriterSettings, model))
{
    var orderResource = new ODataResource
    {
        TypeName = typeof(Order).FullName,
        Id = new Uri("http://tempuri.org/Orders(1)"),
        Properties = new List<ODataProperty>
        {
            new ODataProperty { Name = "Id", Value = 1 },
            new ODataProperty { Name = "Amount", Value = Color.Black },
            new ODataProperty { Name = "OrderDate", Value = Color.White }
        }
    };

    var resourceWriter = await messageWriter.CreateODataResourceWriterAsync(ordersEntitySet, orderEntityType);

    await resourceWriter.WriteStartAsync(orderResource);
    await resourceWriter.WriteEndAsync();

    tempStream.Position = 0;
    var result = await new StreamReader(tempStream).ReadToEndAsync();

    // result:
    // {
    //     "@odata.context":"http://tempuri.org/$metadata#Orders/$entity",
    //     "@odata.id":"http://tempuri.org/Orders(1)",
    //     "Id":1,
    //     "Amount":"Black",
    //     "OrderDate":"White"
    // }
}

Expected result

The library should flag that the value being written onto the primitive property is invalid

Actual result

The library does not flag that the value being written onto the primitive property is invalid

Additional detail

Here's the definition of the InMemoryMessage type:

internal class InMemoryMessage : IODataResponseMessage, IODataResponseMessageAsync, IODataRequestMessage, IODataRequestMessageAsync        
{
    private readonly Dictionary<string, string> headers;

    public InMemoryMessage()
    {
        headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    }

    public IEnumerable<KeyValuePair<string, string>> Headers
    {
        get { return this.headers; }
    }

    public int StatusCode { get; set; }

    public Uri Url { get; set; }

    public string Method { get; set; }

    public Stream Stream { get; set; }

    public string GetHeader(string headerName)
    {
        return this.headers.TryGetValue(headerName, out string headerValue) ? headerValue : null;
    }

    public void SetHeader(string headerName, string headerValue)
    {
        headers[headerName] = headerValue;
    }

    public Stream GetStream()
    {
        return this.Stream;
    }

    public Task<Stream> GetStreamAsync()
    {
        TaskCompletionSource<Stream> taskCompletionSource = new TaskCompletionSource<Stream>();
        taskCompletionSource.SetResult(this.Stream);
        return taskCompletionSource.Task;
    }
}
xuzhg commented 1 week ago

https://github.com/OData/odata.net/blob/main/src/Microsoft.OData.Core/WriterValidator.cs#L140C26-L140C58