-
Notifications
You must be signed in to change notification settings - Fork 944
Clean up some serializer stuff. #1732
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
Conversation
Add app to master list of components in `package.json`
v5.10.1
1. The switch to protobufjs 6.x seems to have removed the need for hasTag() (and actually it had broken our usage of it since the tags ended up changing from snake_case to camelCase). So I've removed hasTag() and set `oneofs: false` in protoLoaderOptions so the virtual "fooType" fields are no longer added. 2. 'seconds' in timestamps should always be serialized as a string, since it's an int64 but we weren't doing that. I fixed it, which removed some of the special casing from rsgowman/grpc_load. 3. I reworked the expect...RoundTrip methods into a single unified one which saves some repetition and I think is easier to make sense of. 4. I re-added the disabled useProto3Json assert and instead tweaked the offending test not to try to round-trip through protobufjs.
// tslint:disable-next-line:no-any | ||
const type = (obj as any)['value_type']; | ||
if (hasTag(obj, type, 'nullValue')) { | ||
if ('nullValue' in obj) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By just looking for the presence of fooValue in obj, rather than looking at value_type, we leave ourselves open to a proto message containing (eg) both nullValue and booleanValue (or any other combination that you like), implying that if we receive a corrupt proto, we'll end up (attempting to) parse it as whatever type comes first in this list. (Or will protobufjs notice that this is a oneof field and throw an error or otherwise do something intelligent?)
I don't have any objection to this; but thought it worth pointing out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd consider it a bug in protobufjs to have more than one value for a one_of field and I don't think we can gracefully handle it. If we wanted to be paranoid we could try to detect / throw, but I'm not sure if using value_type in that case would be a particularly better behavior, so I am inclined to leave it as-is.
For a bit of added context in case it's not clear:
- JSON protos received via WebChannel (i.e. useProto3Json=true) never had value_type, so they were subject to this same potential corruption.
- Prior to protobufjs 6.x, one_of fields would contain all of the values and so using value_type was the only way to tell which was the actual value. That's fixed now, hence no longer a need to use value_type.
- The hasTag() implementation was a pretty big wart to gracefully handle both protobufJs and proto3Json. I think getting rid of it is a big improvement. :-)
const roundTripProto: api.Value = ValueMessage.toObject( | ||
ValueMessage.create({ [tag]: messageField }), | ||
protoLoaderOptions | ||
function verifyFieldValueRoundTrip(opts: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we talked about separating these concerns into multiple tests... which I didn't quite do. I had a single test for each type that called 3 helpers (rather than 3 completely separate tests). This is going a step further and merging all three things into a single helper. But those 3 things are obviously very closely related, and I do like the results.
However, the protobufJsValue seems a little de-emphasized. It's a bit of an odd thing to test, since it's really protobufjs's responsibility to encode the json proto into something that can be sent over the wire. Calling this out more explicitly would be helpful. The best way would probably be a separate test (or at least separate helper) but making this more explicit in the docstring might be sufficient too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I actually have slightly mixed feelings over whether we really are benefiting from testing the protobufJs values, so de-emphasizing it was perhaps slightly intentional. :-) But I do think a better comment is a good idea. Done.
); | ||
expectFullRoundTrip(value, serializer); | ||
const obj = serializer.toValue(new fieldValue.BlobValue(example)); | ||
expect(obj).to.deep.equal(expected); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undoing the call to verifyFieldValueRoundTrip essentially means we never test the round trip of a fieldValue.BlobValue with proto3. Is this ok? (I'm not sure how/where we use proto3json, so I wasn't sure.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, that's a fair point. I thought I was reverting this to the state in master but I forgot there's a "converts BlobValue from string" test in master that was removed in your PR... That said, I think your PR wasn't testing the full round-trip either, since it included round-tripping through protobufJS which yields a UInt8Array instead of base64 string (and this is why you had to remove that assert in serializer.ts).
So I tweaked verifyFieldValueRoundTrip() to accept a useProto3Json
option that uses the proto3 JSON serializer and skips the round-trip through protobufJS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback! Made some tweaks. Let me know what you think.
// tslint:disable-next-line:no-any | ||
const type = (obj as any)['value_type']; | ||
if (hasTag(obj, type, 'nullValue')) { | ||
if ('nullValue' in obj) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd consider it a bug in protobufjs to have more than one value for a one_of field and I don't think we can gracefully handle it. If we wanted to be paranoid we could try to detect / throw, but I'm not sure if using value_type in that case would be a particularly better behavior, so I am inclined to leave it as-is.
For a bit of added context in case it's not clear:
- JSON protos received via WebChannel (i.e. useProto3Json=true) never had value_type, so they were subject to this same potential corruption.
- Prior to protobufjs 6.x, one_of fields would contain all of the values and so using value_type was the only way to tell which was the actual value. That's fixed now, hence no longer a need to use value_type.
- The hasTag() implementation was a pretty big wart to gracefully handle both protobufJs and proto3Json. I think getting rid of it is a big improvement. :-)
const roundTripProto: api.Value = ValueMessage.toObject( | ||
ValueMessage.create({ [tag]: messageField }), | ||
protoLoaderOptions | ||
function verifyFieldValueRoundTrip(opts: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I actually have slightly mixed feelings over whether we really are benefiting from testing the protobufJs values, so de-emphasizing it was perhaps slightly intentional. :-) But I do think a better comment is a good idea. Done.
); | ||
expectFullRoundTrip(value, serializer); | ||
const obj = serializer.toValue(new fieldValue.BlobValue(example)); | ||
expect(obj).to.deep.equal(expected); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, that's a fair point. I thought I was reverting this to the state in master but I forgot there's a "converts BlobValue from string" test in master that was removed in your PR... That said, I think your PR wasn't testing the full round-trip either, since it included round-tripping through protobufJS which yields a UInt8Array instead of base64 string (and this is why you had to remove that assert in serializer.ts).
So I tweaked verifyFieldValueRoundTrip() to accept a useProto3Json
option that uses the proto3 JSON serializer and skips the round-trip through protobufJS.
Manually merged (with history) to rsgowman/grpc_load. |
Sorry, I decided to review #1669 in the form of a PR. 😄 Thanks for doing the hard work to get protobufjs upgraded and the tests working again. This is basically a cleanup pass to convince myself that everything was working right and simplify the tests. In particular:
oneofs: false
in protoLoaderOptions so the virtual "fooType" fields are no longer added at all.