Fix JSON encoding of nested message fields corresponding to query params. (#321) * Add example of a nested message field query param to samples. * Fix JSON encoding of nested message fields corresponding to query params. Note that apitools already has logic to add custom JSON encodings in the generated messages file for message fields whose names differ from their eventual JSON representations: https://github.com/google/apitools/blob/c7d44d6bef513e82fe2b8740352e5e2290dd2e5c/apitools/gen/extended_descriptor.py#L220-L225 Examples can be found throughout the generated message files, e.g. https://github.com/google/apitools/blob/c7d44d6bef513e82fe2b8740352e5e2290dd2e5c/samples/iam_sample/iam_v1/iam_v1_messages.py#L955-L956. This happens here when writing the file: https://github.com/google/apitools/blob/c7d44d6bef513e82fe2b8740352e5e2290dd2e5c/apitools/gen/extended_descriptor.py#L162-L171 Specifically, in AddDescriptorFromSchema, the ExtendedMessageDescriptor objects that are instantiated will contain field_mappings for any field whose converted Python attribute name in the message differs from its name in the discovery doc: https://github.com/google/apitools/blob/c7d44d6bef513e82fe2b8740352e5e2290dd2e5c/apitools/gen/message_registry.py#L278-L281 This all works correctly for explicit message types defined in the API protos (which end up in the "schemas" field of the discovery doc). However, apitools also uses AddDescriptorFromSchema to generate top-level request message types (which don't correspond to any underlying discovery doc schema message): https://github.com/google/apitools/blob/c7d44d6bef513e82fe2b8740352e5e2290dd2e5c/apitools/gen/service_registry.py#L273 To do so, it basically creates a schema dictionary on the fly that matches the one used in the discovery doc JSON and includes the properties for each field, and then passes that to AddDescriptorFromSchema. The problem is that when creating this pseudo-schema, it determines the field name property for query params by calling self.__names.CleanName() on the parameter name: https://github.com/google/apitools/blob/c7d44d6bef513e82fe2b8740352e5e2290dd2e5c/apitools/gen/service_registry.py#L288 But this means that the field name is already in its converted Python form when AddDescriptorFromSchema sees it, so the generated field name matches the field name passed to it, and no custom field mapping gets added even if there should be one. In particular, for nested message query params, the JSON parameter will be something like e.g. options.requestedPolicyVersion, whereas the Python name will be e.g. options_requestedPolicyVersion, so a custom JSON encoding *should* be generated for that field, but isn't (similarly for query params that conflict with Python keywords e.g. 'continue' <-> 'continue_'). The fix here is just deleting the call to self.__names.CleanName() in __CreateRequestType; AddDescriptorFromSchema already calls that when constructing the field (https://github.com/google/apitools/blob/c7d44d6bef513e82fe2b8740352e5e2290dd2e5c/apitools/gen/message_registry.py#L329-L332) and thus there's no need to call it ahead of time. This way, AddDescriptorFromSchema will receive the original JSON parameter name, and can see that it differs from the generated Python field name, so it will now add a custom field mapping for any such query param fields in the request message. Note that this bug has been present in apitools for as long as it has existed, dating back to the very first version. This has necessitated a lot of manual calls to encoding.AddCustomJsonFieldMapping() to work around it in consumers of apitools; these can now all be cleaned up since they'll be included in the generated messages file directly. In theory this shouldn't break anything either; the behavior already was broken and requests would fail without the workaround, and the workarounds themselves won't affect anything even if they're not cleaned up after this fix (it's okay to add a custom JSON field mapping twice as long as both mappings are the same: https://github.com/google/apitools/blob/c7d44d6bef513e82fe2b8740352e5e2290dd2e5c/apitools/base/py/encoding_helper.py#L676-L685).
4 files changed