The context is that I am working on a configurable mock gRPC server which will run in a Docker instance and serve as a mock instance of backend services. I initialize each instance with a proto file and a configuration that tells it what functions to listen to and what data to respond with.
I am using @grpc/grpc-js
and @grpc/proto-loader
. I've already set it up to return arbitrary scalar values as well as arrays and objects with a known schema. Everything was working until I got to the part where I wanted to generate arbitrary objects (i.e. maps). For some reason, the gRPC server doesn't recognize the object that I pass to it as an object and complains.
Error as reported from the client:
Error: 13 INTERNAL: Error serializing response: .Result.testObject: object expected
at callErrorFromStatus (***/test/grpc/node_modules/@grpc/grpc-js/build/src/call.js:31:19)
at Object.onReceiveStatus (***/test/grpc/node_modules/@grpc/grpc-js/build/src/client.js:192:76)
at Object.onReceiveStatus (***/test/grpc/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:360:141)
at Object.onReceiveStatus (***/test/grpc/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:323:181)
at ***/test/grpc/node_modules/@grpc/grpc-js/build/src/resolving-call.js:99:78
at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
for call at
at ServiceClientImpl.makeUnaryRequest (***/test/grpc/node_modules/@grpc/grpc-js/build/src/client.js:160:32)
at ServiceClientImpl.<anonymous> (***/test/grpc/node_modules/@grpc/grpc-js/build/src/make-client.js:105:19)
at file://***/test/grpc/index.mjs:14:10
at file://***/test/grpc/index.mjs:18:3
at ModuleJob.run (node:internal/modules/esm/module_job:217:25)
at async ModuleLoader.import (node:internal/modules/esm/loader:316:24)
at async loadESM (node:internal/process/esm_loader:34:7)
at async handleMainPromise (node:internal/modules/run_main:66:12) {
code: 13,
details: 'Error serializing response: .Result.testObject: object expected',
metadata: Metadata {
internalRepr: Map(2) { 'content-type' => [Array], 'date' => [Array] },
options: {}
}
}
This is my proto file (identical for both client and server):
syntax = "proto3";
service TestApi {
rpc Execute(None) returns (Result);
}
message None {}
message Result {
map<string, Value> testObject = 1;
}
message Value {
oneof kind {
bool boolValue = 1;
int32 intValue = 2;
double doubleValue = 3;
string stringValue = 4;
}
}
The server handler function:
export function handler(call, callback) {
try {
const response = generateValues('$', outputs); // Generate random response
console.log(response);
callback(null, response);
} catch (e) {
console.error(e);
callback(e, null);
}
}
The console.error
never fires. An example of the console.log
output:
{
testObject: {
VmZWD: false,
VXiRwi: 28.087891844342792,
cjCTDFD: false,
inYZS: 6,
AYKahk: ' xsyflf szsnwb ush',
pgUT: 23.19160119367565
}
}
Things I've tried which resulted in the same error:
Value
to google.protobuf.Any
(with associated import)json
, object
, and oneOf
options of the proto-loader
load function to true or false.Note that changing map
to another structure isn't an option as the production protos that this image is meant to be able to mock already use map
in their definitions.
The object you are generating does not match the message types you have defined. The testObject
map field has the value type Value
, which is defined as a message containing one of several fields. In your example response object, the key VmZWD
has the value false
, but a Value
message that represents the value false
would be represented as { boolValue: false }
. All of the map entries have the same problem.