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

Cannot access entity with Edm.Binary key #187

Closed
inxonic opened this issue Jan 1, 2022 · 4 comments
Closed

Cannot access entity with Edm.Binary key #187

inxonic opened this issue Jan 1, 2022 · 4 comments
Labels
bug Something isn't working

Comments

@inxonic
Copy link
Contributor

inxonic commented Jan 1, 2022

Entity URL segment is generated as EntityNameSet(BASE64) though the server expects EntityNameSet(binary'BASE16').

Reproducer:

items = entity_sets.EntityNameSet.get_entities().select('Guid').top(1).execute()
print(items[0].Description)

Debug output here.

@phanak-sap
Copy link
Contributor

phanak-sap commented Jan 1, 2022

Thanks, that's good.

Even better would be with metadata file, as something that can be actually run locally, for example like https://github.com/phanak-sap/pyodata-issue-files/tree/master/%23142. Feel free to add PR to that repository, with new folder "#187" so its easy to link with this particular issue

@phanak-sap phanak-sap added the enhancement New feature or request label Jan 1, 2022
@inxonic
Copy link
Contributor Author

inxonic commented Jan 3, 2022

Okay, I'd like to answer this #186 (comment) here, as that's the more appropriate place and any effort to fix this issue probably needs a new PR.

But correct encoding of Edm.Binary as parameters in URL should be covered as well, right?

Of course it should be correct in all components of the URL. According to URI Conventions there could be two place in the URI with value content: The Key Value in the Resource Path (3.1.) and Filter System Query Option (4.5.). Both cases refer to the so called Literal, that is specific to the Primitive Data Type.

The "binary encoding" of URL is really something to handle in the service.py module and the ODataHttpRequest, since URI encoding is not part of the odata metadata and odata model per se. I see it as basic separation of responsibility. Consider hypothetical possibility of supporting the same odata metadata, but with query the server with different than HTTP protocol (it is not yet existing something like this and probably never be, but let's say Odata on something like Gopher or IPFS protocol)

Full agreement on the separation of concerns, thanks for pointing that out. With that in mind, maybe it wasn't precise enough to call this an URL generation issue. Actually it's an issue with the generation of the so called Literal.

And I'm afraid this can't be fixed in the ODataHttpRequest class because its methods only ever get the URL parts as string components. They'd have to look up the data types of the contained entities again to know, how they need to be manipulated.

So for the key value the path component is fetched from

def get_path(self):
return self._entity_set_proxy.last_segment + self._entity_key.to_key_string()
which then calls to_literal in the EntityKey class
f'{key_prop.name}={key_prop.to_literal(self._proprties[key_prop.name])}')

The same is true for the filter value in the query component, that calls to_literal in the GetEntitySetFilter

return f'{proprty.name} {operator} {proprty.to_literal(value)}'

So if anybody is ever going to specifiy OData V2 over another fancy protocol, there'd still be an adressing scheme, that would probably also refer to the type depended literals. And that makes this issue touch the model domain.
Or put in other words, if the literal shouldn't be inside the model domain, then why are the TypTraits classes and their to_literal methods.

@phanak-sap
Copy link
Contributor

phanak-sap commented Jan 4, 2022

OK, "OData V2 over another fancy protocol" will probably never happen. I was trying to illustrate a point about separation, but not stating a valid, although hypothetical requirement. My bad. So let's consider to stick OData V2 only. And with that it mind, even the only encoding/decoding strictly base64-base16 will suffice for now.

You are also correct with all the comments about service.py:218 and service.py:990, the ODataHttpRequest is not that great place after all.

So I would probably continue with #186 as currently is, by adding/modifying tests and call it a day :)

Option 2 defined there ("Values are stored in their Python native representation") is more dangerous. The backward compatibility break is a reason enough not to do it. But also (maybe I am again missing something), in the end, there would need to be basically same difference in binary encoding for the reading of the value (e.g. generating POST body) and for the to_literal() method (generating URL key/params), as in Option 1; so in the end, option 1 with extra steps. Am I Right?

@inxonic
Copy link
Contributor Author

inxonic commented Jan 5, 2022

I was trying to illustrate a point about separation, but not stating a valid, although hypothetical requirement. My bad.

That's allright. I got the point.

in the end, there would need to be basically same difference in binary encoding for the reading of the value (e.g. generating POST body) and for the to_literal() method (generating URL key/params)

Agree, so for Option 1 that means: to/from_json does nothing and to/from_literal does base64 to base16 and vice versa.

So I would probably continue with #186

Okay, I will dig into the tests.

@phanak-sap phanak-sap added bug Something isn't working and removed enhancement New feature or request labels Jan 11, 2022
filak-sap pushed a commit that referenced this issue Jan 11, 2022
While Edm.Binary values are Base64 encoded in JSON payload [1][1], the
specification demands a prefixed and quoted Base16 encoding for values in URIs
and HTTP headers [2][2].

As there has been no specific conversion for Edm.Binary so far and we want to
stay backward compatible, the Python side representation shall remain a string
with the Base64 encoded payload.

However there are cases, where the literal is generated from the payload and
sent to the server, that expects it in the correct format.  E.g. this happens
when building the resource path while using a Edm.Binary property as an entity
key (see #187 and phanak-sap/pyodata-issue-files#2).

Another case might be when using the $include filter for a Edm.Binary property.

To meet those requirements, we want to have these representations:

 JSON   | Python       | Literal
--------|--------------|------------------------
 Base64 | str (Base64) | prefixed quoted Base16

This adds a specific type for Edm.Binary with the necessary conversions.

Further it extends the test cases to cover those conversions.

As the Edm.Binary type has been used to test the generic prefixed conversions,
we need to switch this to the Edm.Byte type, that remains generic.

[1](https://www.odata.org/documentation/odata-version-2-0/json-format)
[2](https://www.odata.org/documentation/odata-version-2-0/overview/)
@inxonic inxonic closed this as completed Jan 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants