Search code examples
asp.net-core.net-coregrpcgrpc-dotnet

Unexpected behaviour with FieldMask using dotnet core gRPC


From grpc/grpc-dotnet issue #2097

I am new to gRPC and I am not sure if I am implementing the FieldMask in the correct way.

Here are my messages definitions

// Events messages
message Event {
  // optional string id = 1 [deprecated = true];
  reserved 1;
  optional string name = 2;
  optional string description = 3;
 // ...
  optional string locationId = 21;
}

message UpdateEventRequest {
  string id = 1;
  Event event = 2;
  google.protobuf.FieldMask updateMask = 3;
}

and here is my csharp code

    public override async Task<EventResponse> UpdateEvent(UpdateEventRequest request, ServerCallContext context)
    {

        if (!_eventRepository.Exists(request.Id, out var evt) || evt is null)
        {
            throw new RpcException(new Status(StatusCode.NotFound, "Entity not found"));
        }
        
        var dto = evt.AdaptToDto();
    
        request.UpdateMask.Merge(request.Event, dto);

        await new EventValidator().ValidateAndThrowAsync(dto);

        evt = dto.AdaptToPoco(evt);
        var success = _eventRepository.UpdateEvent(evt);
        
        if (!success) throw new RpcException(new Status(StatusCode.NotFound, "Entity not updated."));

        return new EventResponse
        {
            Id = evt.Id,
            Event = dto
        };
    }

I am using .NET7 and Grpc.AspNetCore 2.52.0

when I try to update a field that has pascal-case (like locationId) I face this wired error that occurs with some clients, when I send this request

{
    "id": "fd416fa0-5794-4bdc-be9e-1feece4f7cac",
    "event": {
        "locationId": "e3362b3b-2b8d-42cd-8c63-a5d9d9d46597"
    },
    "updateMask": "locationId"
}

in the path locationId get translated to location_id, where the merge fails.

debug using string

but I am sending the following request

{
    "id": "fd416fa0-5794-4bdc-be9e-1feece4f7cac",
    "event": {
        "locationId": "e3362b3b-2b8d-42cd-8c63-a5d9d9d46597"
    },
    "updateMask": {
        "paths": [
            "locationId"
        ]
    }
}

the merge works but there is a warning inside the FieldMask

debug using path

Did I do something wrong? Is this the expected behavior, and how can I fix it?


Solution

  • This comment on the source-code of FieldMask points that:

    Fields name in each path are converted to/from lower-camel naming conventions.

    (a.k.a snake_case)

    And in the examples you see they use full paths, not only the field they want to update but they also specify the message that contains it if any.

    So, with your current JSON request I think you are technically requesting the update of a "root-level" field called location_id.

    Consider using location_id as a field-name to align with the style-guide or specify a json_name (assuming you are using proto3) like this:

    message Event {
      // optional string id = 1 [deprecated = true];
      reserved 1;
      optional string name = 2;
      optional string description = 3;
      // ...
      optional string locationId = 21 [json_name = "location_id"];
    }
    

    And making the request like this should get it working:

    {
        "id": "fd416fa0-5794-4bdc-be9e-1feece4f7cac",
        "event": {
            "locationId": "e3362b3b-2b8d-42cd-8c63-a5d9d9d46597"
        },
        "updateMask": {
            "paths": [
                "event.locationId"
            ]
        }
    }