Response message including the result

i’m following the documentation here

and have this in my protobuf service definition

message ListUsersResponse {
  repeated UserViewState results = 1;
}

service ListUsers {
  option (kalix.codegen) = {
    view: {}
  };

  rpc UpdateUser(bet.gente.user.domain.UserState) returns (UserViewState) {
    option (kalix.method).eventing.in = {
      value_entity: "user"
    };
    option (kalix.method).view.update = {
      table: "users_list"
      transform_updates: true
    };
  }

  rpc ListUsers(google.protobuf.Empty) returns (ListUsersResponse) {
    option (kalix.method).view.query = {
      query: "SELECT * AS results FROM users_list"
    };
    option (kalix.method).acl = {
      allow: { principal: ALL }
    };
  }
}

and the method that transforms UserState => UserViewState is

override def updateUser(state: UserViewState, userState: UserState): UpdateEffect[UserViewState] = {
    val viewState = UserViewState(userId = userState.userId, name = userState.name, email = userState.email)
    effects.updateState(viewState)
  }

However, i ALWAYS get a 400 response from a HTTP request:

Expect message object but got: null

And the result of a gRPC request to ListUsers looks like this:

{
  "user_id": "\n\u0006trevor\u0012\u000etrevor@eli.com\u001a\u0006trevor",
  "email": "",
  "name": ""
}

I’m clearly missing something here but i can’t find it in the documentation - can anyone show me where i’m making the mistake?

It looks like the request you’re doing there might not be correct… could you show us how the request and what the corresponding proto looks like on the User ValueEntity? The code for the view looks OK as far as I can see, so I would expect the issue to be on the source of the view (the User ValueEntity).

thanks for your reply - sure, here’s the proto for the value entity:

message User {
  string user_id = 1 [(kalix.field).entity_key = true];
  string email = 2;
  string name = 3;
}

service UserService {
  option (kalix.codegen) = {
    value_entity: {
      name: "bet.gente.user.domain.User"
      entity_type: "user"
      state: "bet.gente.user.domain.UserState"
    }
  };

  rpc CreateUser(User) returns (google.protobuf.Empty) {
    option (kalix.method).acl = {
      allow: { principal: ALL }
    };
  }
  rpc GetUser(GetUserRequest) returns (User) {}
  rpc ChangeName(ChangeNameRequest) returns (google.protobuf.Empty) {}
}

and the UserState is:

message UserState {
  string user_id = 1;
  string email = 2;
  string name = 3;
}

I’m sending the CreateUser HTTP command from Postman - the code version is:

curl --location --request POST 'http://localhost:9000/bet.gente.user.api.UserService/CreateUser' \
--header 'Content-Type: application/json' \
--data-raw '{
    "user_id": "trevor",
    "name": "trevor",
    "email": "trevor@eli.com"
}'

and i’m using BloomRPC for the ListUsers request because grpcurl doesn’t seem to like the local setup…

grpcurl -plaintext localhost:9000 list

gives me

bet.gente.user.api.UserService

but

grpcurl -plaintext localhost:9000 describe bet.gente.user.api.UserService

gives me

Failed to resolve symbol "bet.gente.user.api.UserService": Symbol not found: bet.gente.user.api.UserService

so i think i’m missing something else there, too because when i do

grpcurl -d '{}' -plaintext localhost:9000 bet.gente.user.view.ListUsers/ListUsers

I get

Error invoking method "bet.gente.user.view.ListUsers/ListUsers": target server does not expose service "bet.gente.user.view.ListUsers"

and yet BloomRPC gives me the result from the message in the original post… i must be doing all sorts of things wrong :woman_shrugging:

sorry, missed the HTTP ListUsers command

curl --location --request POST 'http://localhost:9000/bet.gente.user.view.ListUsers/ListUsers' \
--header 'Content-Type: application/json' \
--data-raw ''

I also tried with an empty json object in case that was what the expected object got null was about

curl --location --request POST 'http://localhost:9000/bet.gente.user.view.ListUsers/ListUsers' \
--header 'Content-Type: application/json' \
--data-raw '{}'

I used the .g8 template which references kalix v1.2.1-M3 - i tried M4 as well as earlier versions going back to v1.2.0 just in case - always the same results as described above

ok, update - the empty object in the body of the HTTP request was missing from Postman’s request - that fixes the HTTP version when i curl the ListUsers endpoint i get

{
    "results": [
        {
            "id": "trevor2",
            "email": "trevor2@eli.com",
            "name": "trevor"
        },
        {
            "id": "eli",
            "email": "eli@eli.com",
            "name": "eli"
        }
    ]
}

the results array contains all the users…

the gRPC response, though, not so much… hitting ListUsers using grpcurl i get:

$ grpcurl -d '{}' -plaintext localhost:9000 bet.gente.user.view.ListUsers/ListUsers
Error invoking method "bet.gente.user.view.ListUsers/ListUsers": target server does not expose service "bet.gente.user.view.ListUsers"

It’s been a while since i worked with gPRC endpoints and didn’t realise BloomRPC was deprecated :face-palm - but Postman gPRC works just fine so there’s something i’m missing about that grpcurl command, it seems

T

Tried a few different things locally but cannot really reproduce that behaviour. As in, I can obviously reproduce that error if I provide the wrong path for the service but given you seem to be using the same path for gRPC and HTTP AFAICS, I’m not seeing a reason for it to fail.

Does the view endpoint get listed when you do grpcurl --plaintext localhost:9000 list ?

If you have the chance to share the full code or a smaller sample that I can reproduce locally, then we could at least know if this is something on kalix or some weird thing with your local setup.

Ii’m able to reproduce the behaviour with the sbt giter8 template - i create a new project with sbt new lightbend/kalix-value-entity.g8 and while grpcurl -plaintext localhost:9000 list gives me

com.example.CounterService
grpc.reflection.v1alpha.ServerReflection

when i do grpcurl -plaintext localhost:9000 describe com.example.CounterService i get:

Failed to resolve symbol "com.example.CounterService": Symbol not found: com.example.CounterService

And when i do grpcurl -d '{"counterId": "foo"}' -plaintext localhost:9000 com.example.CounterService/CurrentCounter (the example command from the docs) i get:

Error invoking method "com.example.CounterService/CurrentCounter": target server does not expose service "com.example.CounterService"

It seems like this is down to an issue on grpcurl itself. We’ve managed to reproduce this if using grpcurl 1.8.1 but once upgraded to grpcurl 1.8.7 it works as expected. (I’m not certain from which version the fix is included but it works as expected with 1.8.7).

Yep, that’s got it!

Many thanks