Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IfNoneMatch.ApplyTo does not work for byte[] fields. #1332

Open
anasik opened this issue Oct 28, 2024 · 1 comment
Open

IfNoneMatch.ApplyTo does not work for byte[] fields. #1332

anasik opened this issue Oct 28, 2024 · 1 comment
Labels
bug Something isn't working

Comments

@anasik
Copy link

anasik commented Oct 28, 2024

Assemblies affected
ASP.NET Core OData 8.x

Describe the bug
If I have a byte[] RowVersion field and I check for and apply queryOptions.ifNoneMatch.ApplyTo, the query remains unaffected and therefore doesn't filter out the records that do match.

Reproduce steps
Let's modify minimally following E2E test already exists: Microsoft.AspNetCore.OData.E2E.Tests.ETags.CRUDWithIfMatchETagsTest.GetEntityWithIfNoneMatchShouldReturnNotModifiedETagsTest

  1. In the ETagsModel file, on Line 40 change the type of the field StringWithConcurrencyCheckAttributeProperty from string to byte[]

  2. Then, in EtagsController, in the Helpers.CreateCustomer method on line 180, initialize this field as the following:
    StringWithConcurrencyCheckAttributeProperty = new byte[] {0, 0, 0, 0, 0, 0, 16, 28},

    a. In case you're afraid of some of the other concurrency fields affecting this, feel free to go to the
    CRUDWithIfMatchETagsTest.GetEdmModel method and comment out lines 43 and 44 so there's only one concurrency field.

  3. Now, if you run this test, it will fail. If you debug further, you'll notice that the queryOptions.ifNoneMatch.ApplyTo call in EtagsController has absolutely no effect on appliedCustomers

Data Model

public class ETagsCustomer
    {
        public int Id { get; set; }
        public string Name { get; set; }
        
        TRUNCATED SOME FIELDS THAT DON'T AFFECT THIS CASE

        [ConcurrencyCheck]
        public byte[] StringWithConcurrencyCheckAttributeProperty { get; set; }
        public ETagsCustomer RelatedCustomer { get; set; }
        [Contained]
        public ETagsCustomer ContainedCustomer { get; set; }
    }

EDM (CSDL) Model
This example involves using the existing E2E Etags test so I'm not sure how to extract metadata from there without complicating things but here's the code that generates the EDM Model, copy-pasted shamelessly from the file.

private static IEdmModel GetEdmModel()
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            EntitySetConfiguration<ETagsCustomer> eTagsCustomersSet = builder.EntitySet<ETagsCustomer>("ETagsCustomers");
            EntityTypeConfiguration<ETagsCustomer> eTagsCustomers = eTagsCustomersSet.EntityType;
            // eTagsCustomers.Property(c => c.Id).IsConcurrencyToken();    
            // eTagsCustomers.Property(c => c.Name).IsConcurrencyToken();
            return builder.GetEdmModel();
        }

I commented out those two lines to isolate the problem. The bug can be recreated regardless of the existence of these lines.

Request/Response
Again, I'm gonna have to copy code. It's really just the GetEntityWithIfNoneMatchShouldReturnNotModifiedETagsTest

Click me
public async Task GetEntityWithIfNoneMatchShouldReturnNotModifiedETagsTest()
        {
            // Arrange
            HttpClient client = CreateClient();
            string eTag;

            // DeleteUpdatedEntryWithIfMatchETagsTests will change #"0" customer
            // PutUpdatedEntryWithIfMatchETagsTests will change #"1"customer
            // PatchUpdatedEntryWithIfMatchETagsTest will change #"2" customer
            // So, this case uses "4"
            int customerId = 4;
            var getUri = "odata/ETagsCustomers?$format=json";

            // Act
            using (var response = await client.GetAsync(getUri))
            {
                // Assert
                Assert.True(response.IsSuccessStatusCode);

                var json = await response.Content.ReadAsObject<JObject>();
                var result = json.GetValue("value") as JArray;
                Assert.NotNull(result);

                eTag = result[customerId]["@odata.etag"].ToString();
                Assert.False(string.IsNullOrEmpty(eTag));
            }

            // Arrange & Act
            var getRequestWithEtag = new HttpRequestMessage(HttpMethod.Get, $"odata/ETagsCustomers({customerId})");
            getRequestWithEtag.Headers.IfNoneMatch.ParseAdd(eTag);
            using (var response = await client.SendAsync(getRequestWithEtag))
            {
                // Assert
                Assert.Equal(HttpStatusCode.NotModified, response.StatusCode);
            }
        }

Expected behavior
This test should pass. Since this field is never changed.

Screenshots
N/A

Additional context
In order of importance:

  1. I have been debugging this for a while and here's the only useful observation I have made:
    In Microsoft.AspNetCore.OData.Query.ETag, on line 148, if we remove the following block:
  if (IsIfNoneMatch)
    {
        where = Expression.Not(where);
    }

It does send back a 304 Not Modified and does pass the test.

  1. I first discovered this while working on my project. I was using this test as a reference to make sure I was using etags correctly. After failing to purposefully produce a 304 Not Modified response by passing in an If-None-Match header, I decided to see if using a byte[] field makes the E2E test fail. And I was right.
@anasik anasik added the bug Something isn't working label Oct 28, 2024
@anasik
Copy link
Author

anasik commented Oct 28, 2024

I understand that I cheated a lot on the reproduction part of this bug. I chose to do this because:

  1. I genuinely thought this was more convenient for the community.
  2. This is, in fact, the simplest way to reproduce the bug for any contributor.

If the maintainers/contributors disagree with me on this, I would be more than willing to provide more information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant