Skip to content

Commit 3208e24

Browse files
inxonicfilak-sap
authored andcommitted
Fix Edm.Binary literal representation
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/)
1 parent 7d65f74 commit 3208e24

File tree

2 files changed

+27
-5
lines changed

2 files changed

+27
-5
lines changed

pyodata/v2/model.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"""
77
# pylint: disable=missing-docstring,too-many-instance-attributes,too-many-arguments,protected-access,no-member,line-too-long,logging-format-interpolation,too-few-public-methods,too-many-lines, too-many-public-methods
88

9+
import base64
910
import collections
1011
import datetime
1112
from enum import Enum, auto
@@ -194,7 +195,7 @@ def _build_types():
194195
Types.Types = {}
195196

196197
Types.register_type(Typ('Null', 'null'))
197-
Types.register_type(Typ('Edm.Binary', 'binary\'\''))
198+
Types.register_type(Typ('Edm.Binary', 'binary\'\'', EdmBinaryTypTraits('(?:binary|X)')))
198199
Types.register_type(Typ('Edm.Boolean', 'false', EdmBooleanTypTraits()))
199200
Types.register_type(Typ('Edm.Byte', '0'))
200201
Types.register_type(Typ('Edm.DateTime', 'datetime\'2000-01-01T00:00\'', EdmDateTimeTypTraits()))
@@ -361,6 +362,18 @@ def from_literal(self, value):
361362
return matches.group(1)
362363

363364

365+
class EdmBinaryTypTraits(EdmPrefixedTypTraits):
366+
"""Edm.Binary traits"""
367+
368+
def to_literal(self, value):
369+
binary = base64.b64decode(value, validate=True)
370+
return f"binary'{base64.b16encode(binary).decode()}'"
371+
372+
def from_literal(self, value):
373+
binary = base64.b16decode(super().from_literal(value), casefold=True)
374+
return base64.b64encode(binary).decode()
375+
376+
364377
class EdmDateTimeTypTraits(EdmPrefixedTypTraits):
365378
"""Emd.DateTime traits
366379

tests/test_model_v2.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,19 @@ def test_traits():
367367
"""Test individual traits"""
368368

369369
# generic
370-
typ = Types.from_name('Edm.Binary')
370+
typ = Types.from_name('Edm.Byte')
371371
assert repr(typ.traits) == 'TypTraits'
372-
assert typ.traits.to_literal('bincontent') == 'bincontent'
373-
assert typ.traits.from_literal('some bin content') == 'some bin content'
372+
assert typ.traits.to_literal('85') == '85'
373+
assert typ.traits.from_literal('170') == '170'
374+
375+
# binary
376+
typ = Types.from_name('Edm.Binary')
377+
assert repr(typ.traits) == 'EdmBinaryTypTraits'
378+
assert typ.traits.to_literal('wAHK/rqt8A0=') == 'binary\'C001CAFEBAADF00D\''
379+
assert typ.traits.from_literal('binary\'C001cafeBAADF00D\'') == 'wAHK/rqt8A0='
380+
assert typ.traits.from_literal('X\'C001cafeBAADF00D\'') == 'wAHK/rqt8A0='
381+
assert typ.traits.to_json('cHlvZGF0YQ==') == 'cHlvZGF0YQ=='
382+
assert typ.traits.from_json('cHlvZGF0YQ==') == 'cHlvZGF0YQ=='
374383

375384
# string
376385
typ = Types.from_name('Edm.String')
@@ -1462,4 +1471,4 @@ def test_invalid_xml(xml_builder_factory):
14621471

14631472
with pytest.raises(PyODataParserError) as e_info:
14641473
MetadataBuilder(xml).build()
1465-
assert str(e_info.value) == 'Metadata document syntax error'
1474+
assert str(e_info.value) == 'Metadata document syntax error'

0 commit comments

Comments
 (0)