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

Missing fields when numeric values have ZERO as their value or strings have empty strings as their value #2362

Closed
styriandev opened this issue Feb 14, 2023 · 2 comments

Comments

@styriandev
Copy link

Problem description

I am facing an interesting problem when using a GRPC service written in Python. It seems like whenever the service is returning ZERO as the value for a numeric field or an empty string for a string field it is not setting some properties of the object. That is a problem because that results in uncomplete objects.
The interesting thing is that when I am using Postman to check the response of the Python service it is clearly visible that the fields are defined, therefore I came to the conclusion that this is a problem of GRPC.

I read through serveral posts like 2254 or 1572 for protobuf.js and I came to the conclusion that this is something that this is clearly a decoding issue. I also did some debugging and will show that in the reproduction steps.

Reproduction steps

We are using NestJs as our main backend framework. That consumes Grpc.js. For writing proto files we are using the proto3 syntax.
An example message where this problem happens looks like that:
message SomeErrorMessage{ string timestamp = 1; int32 error_from = 2; int32 error_to = 3; // can be 0 -> if 0 it is missing in the SomeErrorMessage object string description = 4; int32 error_occurred = 5; -> if '' it is missing in the SomeErrorMessage object }

As stated in the comments for the message, there are 2 fields that are just missing after decoding when they have the JS default value (0 or empty string).

When debugging that through I saw that the decoding is called from the client-interceptors.ts class which contains a "onReceiveMessage" callback. This message is then used in the deserializing process. That process calls a deserialize function from createDeserializer which then calls some generated code. As you can see from the following gif, inside that generated code I am just not getting into the block that would be used to create the new object attribute:

Debugging
Stacktrace

Unfortunally I cannot provide the python service because it is company specific. But I guess the createDeserializer function should handle that in some way, so when the decoding not decodes all properties it fills up the object with the missing attributes. Other libs seem to do that because else Postman would also not include the object properties.

Environment

  • Windows 10
  • Node 16.14.2
  • NPM 8.5.0
  • "@grpc/grpc-js": "1.3.7"
  • "@grpc/proto-loader": "0.6.5"

Additional context

I don't know if that helps but the generated code is the following:

(function anonymous(Reader, types, util
) {
    return function CSErrorTypeAssignment$decode(r, l) {
        if (!(r instanceof Reader))
            r = Reader.create(r)
        var c = l === undefined ? r.len : r.pos + l, m = new this.ctor
        while (r.pos < c) {
            var t = r.uint32()
            switch (t >>> 3) {
                case 1: {
                    m.timestamp = r.string()
                    break
                }
                case 2: {
                    m.errorFrom = r.int32()
                    break
                }
                case 3: {
                    m.errorTo = r.int32()
                    break
                }
                case 4: {
                    m.errorType = r.int32()
                    break
                }
                case 5: {
                    m.description = r.string()
                    break
                }
                case 6: {
                    m.errorOccurred = r.int32()
                    break
                }
                case 7: {
                    m.modificationInfo = types[6].decode(r, r.uint32())
                    break
                }
                case 8: {
                    m.plcName = r.string()
                    break
                }
                default:
                    r.skipType(t & 7)
                    break
            }
        }
        return m
    }
})

After this function call I receive the following object:
image

Postman however returns me the following object:
image

@murgatroid99
Copy link
Member

This is intended behavior. In Protobuf 3, fields with the default value are omitted from serialized messages, to reduce space and bandwidth usage. By default, those fields are also omitted from deserialized message objects. To make it instead include every field, set the defaults option to true in your call to @grpc/proto-loader's load or loadSync function. For more information about available options, check the @grpc/proto-loader npm page.

@styriandev
Copy link
Author

@murgatroid99 Thank you for answering that question! Worked fine for us!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants