-
-
Notifications
You must be signed in to change notification settings - Fork 70
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
fix: correctly handle defaults for proto2 and proto3 #146
fix: correctly handle defaults for proto2 and proto3 #146
Conversation
…x bazel run //src:diff_compiler_update' repeatedly
…run it multiple times and it produces the same output to src/compiler/{descriptor,plugin}.ts every time
…wo lines when rebasing Yurzel's work -- this is now fixed
…optional in a proto3 file, then do not use the default value for that field; also do not use default values for one_of fields, unless the defaults are explicitly provided in the .proto file
…3 optionals) during serialization
…ty fields of type bytes should also be skipped during serialization
…ization conditions in createSerialize()
Good job 👍 Just a few things.
Thanks. This was bugging me alot. Didn't realize this was an issue. 😓
Actually all of my work has been inspired by protoc javascript output. From the observation you can see that almost all the fields use I even tackled the serialization of default values there. presence fields info: |
I wish I had taken a better look at all the pull requests. I clearly should have started with #136 instead of #134. @Yurzel, thanks for telling me about the other pull request. |
@tomasz-szypenbejl-td let's land this and you follow up with the next one? that'd make it easier for me to review |
I don't want to be a speed bump to you since you are putting a lot of effort into this. |
@thesayyn If you prefer to review this pull request first, and then have another pull request containing the changes from #136 - that seems perfectly OK to me. But if during the review you decide that there are some changes that are simply not acceptable and could be easily fixed by including the additional commits from #136 (those ugly |
@tomasz-szypenbejl-td only two concerns withstands now. Let's address them and land this |
Agreed. It may take me a few days to make the necessary changes - please be patient :) |
* make toObject() work just the same as before introducing defaults, * revert test changes that were made due to changed toObject() behaviour, * no more defaults for required proto2 fields without [default=], * tests for the defaults feature modified accordingly
I think I got it right this time (but I do not mind making a few more corrections should that be necessary). The generated toObject() method will now work exactly the same as before (as if the defaults did not exist). People who want toObject() to return defaults can borrow a helper method Please note that eventually I did not touch the There is a test case that verifies that in proto2 normal property access (without the |
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.
Awesome!
BTW, the current behavior toObject is to return defaults since it reads the fields from their getters. if getters return default values so will toObject. |
Oh, I guess that may be a misunderstanding on my part. I thought you wanted the observable behaviour of the Nothing to worry about - I will just change the code generating Thank you for approving and merging this pull request. |
Amazing work – @thesayyn would you be able to do a release including this PR as soon as you have some mental bandwidth for it? This issue hit me in the face today. Cheers mate. |
I have discovered, that toObject doesn't return default for the Map, but the corresponding getter does. Is this a bug or this is by design? |
I think this must be a bug I introduced. The final code in this pull request was not really returning defaults in |
Thank you for the explanation, I'd like to fix it while working on #154 . |
@tomasz-szypenbejl-td Can I remove this function and only refer to
This change does not affect the generation of |
You probably mean the
All I am trying to say is that the change you would like to make is quite big, requires a lot of work and might be a breaking change for some users of the library. Whether such a change will be accepted & merged if of course up to @thesayyn. |
Yeah, sorry, I mean
At first I wanted to write that there is a counterexample in the tests, but noticed that you can pass no object into the constructor. This invalidates the BTW, in the message mentioned above signatures of the constructor and fromObject methods are different. I'd like to bring them into line.
Oh, thank you for clarifying this. Now there is a reason for
Thanks a lot for the reference!
Yeah, i think my old proposal can introduce more bugs. It needs to be corrected, so:
This way protoc-gen-ts will still allow the creation of invalid messages and ready to handle invalid messages with typescript's strictNullCheck enabled. The return type of toObject method will represent actual values returned. Getters and setters types will also represent actual values. Constructor already uses only |
Again, @tomasz-szypenbejl-td, |
@Santalov I think it all makes sense. I only have doubts about:
I think that if a method is called I recently noticed that currently repeated fields are handled rather inconsistently too: const m1 = new DefaultMessageV2WithoutDefault();
// compiles fine
const m2 = new DefaultMessageV2WithoutDefault({});
// does not compile - required fields missing
const m3 = new DefaultMessageV2WithoutDefault({ array_int32: [] });
// does not compile - required fields still missing
const m4 = DefaultMessageV2WithoutDefault.fromObject({});
// does not compile - required field missing
const m5 = DefaultMessageV2WithoutDefault.fromObject({ array_int32: [] });
// compiles fine - apparently it only cares about array_int32 and ignores the required fields here
const m6 = DefaultMessageV2WithoutDefault.fromObject({ array_int32: [], array_message: [] });
// compiles fine - but it does not really care about array_message - as seen in the example above I think this inconsistency (in handling of arrays of messages vs arrays of other types in
No problem at all. Thank you for your work on making this library better. |
Yeah, that makes sense. I have already implemented that feature, but the decision is after @thesayyn
I'll fix this inconsistency in the next pr. Personally, I'd like to make them nullable. |
I would prefer them to be nullable too. Somebody could argue that making them non-nullable and forcing user to always provide at least an empty array (or map) to Of course, even if user skips the array/map fields during message creation, the |
This is another attempt to add the default values feature, based on @Yurzel's work. It is still not perfect, but my company is willing to spend (reasonably) more time on this, provided we receive some feedback/guidance on how to make it better (@td-krzysiek, thanks for letting me work on it).
This pull request contains:
npx bazel run //src:diff_compiler_update
to work. Those changes also made most of the tests pass. The problem was that src/descriptor.ts code was accessing object fields like 'oneof_index' and 'default_value' using the new getters (that are providing default values now), and thus testing against null (and similar operations) did not work as expected. The bad thing is I fixed the problem by using the uglypb.Message.getField(fieldDescriptor, someNumber)
calls - perhaps it is possible to figure out a better approach.getField()
calls, and it produces smaller output. The bad thing is serialization no longer works according to the spec for proto2 messages (as I understand, in proto2 it is OK to skip undefined/null fields, not the fields having default values). I think the problem could be fixed by resorting to the uglygetField()
calls in serialization code generated for proto2 messages.It appears that the main source of the still unsolved problems is that those ugly
getField()
calls have to be made to obtain actual value of a field without the default. Perhaps we should consider making a change in the classes generated for protobuf messages, so that they allow user to get field value both with and without default, e.g.:Then we could easily made the code in src/descriptor.ts pretty again, and easily restore the correct serialization behavior for proto2 messages - just a thought.