Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

- service: let FunctionRequests return a list of EntityProxies instead of the raw json, when the `ReturnType` is a Collection. - Emil B.

## [1.11.2]

### Fixed
Expand Down
16 changes: 16 additions & 0 deletions pyodata/v2/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -1712,6 +1712,22 @@ def function_import_handler(fimport, response):
entity_set = self._service.schema.entity_set(fimport.entity_set_name)
return EntityProxy(self._service, entity_set, fimport.return_type, response_data)

# 1.b alternatively, if return type is a Collection, return a list of appropriate entity proxy
if isinstance(fimport.return_type, model.Collection):
total_count = None
next_url = None
if '__count' in response_data:
total_count = int(response_data['__count'])
if '__next' in response_data:
next_url = response_data['__next']
collection = ListWithTotalCount(total_count, next_url)

entity_set = self._service.schema.entity_set(fimport.entity_set_name)
collection_item_type = fimport.return_type.item_type
for entity in response_data['results']:
collection.append(EntityProxy(self._service, entity_set, collection_item_type, entity))
return collection

# 2. return raw data for all other return types (primitives, complex types encoded in dicts, etc.)
return response_data

Expand Down
2 changes: 1 addition & 1 deletion tests/metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@
m:HttpMethod="GET"/>

<FunctionImport Name="get_best_measurements" ReturnType="Collection(EXAMPLE_SRV.TemperatureMeasurement)"
EntitySet="EXAMPLE_SRV.TemperatureMeasurements" m:HttpMethod="GET"/>
EntitySet="TemperatureMeasurements" m:HttpMethod="GET"/>

<FunctionImport Name="sum" ReturnType="Edm.Int32" m:HttpMethod="GET">
<Parameter Name="A" Type="Edm.Int32" Mode="In"/>
Expand Down
82 changes: 82 additions & 0 deletions tests/test_service_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,88 @@ def test_function_import_entity(service):
assert result.Sensor == 'Sensor-address'
assert result.Value == 456.8

@responses.activate
def test_function_import_collection(service):
"""Function call with collection return type"""

# pylint: disable=redefined-outer-name

responses.add(
responses.GET,
f'{service.url}/get_best_measurements',
headers={'Content-type': 'application/json'},
json={'d': {'results' : [{
'Sensor': 'sensor2',
'Date': "/Date(1776417069000)/",
'Value': '29.8d'
}, {
'Sensor': 'sensor4',
'Date': "/Date(1776417578000)/",
'Value': '30.2d'
}],
}},
status=200)

result = service.functions.get_best_measurements.execute()
assert isinstance(result, list)
assert len(result) == 2

assert isinstance(result[0], pyodata.v2.service.EntityProxy)
assert result[0].Sensor == 'sensor2'
assert result[0].Date == datetime.datetime(2026, 4, 17, 9, 11, 9, tzinfo=datetime.timezone.utc)
assert result[0].Value == 29.8

assert isinstance(result[1], pyodata.v2.service.EntityProxy)
assert result[1].Sensor == 'sensor4'
assert result[1].Date == datetime.datetime(2026, 4, 17, 9, 19, 38, tzinfo=datetime.timezone.utc)
assert result[1].Value == 30.2


@responses.activate
def test_function_import_collection_with_pagination_metadata(service):
"""Function call with collection return type including pagination metadata

This test demonstrates that both __next and __count metadata should be preserved
when a FunctionImport returns a partial Collection with inline count.
Per OData v2 spec, this metadata is part of the Collection response format.
"""

# pylint: disable=redefined-outer-name


responses.add(
responses.GET,
f'{service.url}/get_best_measurements?$inlinecount=allpages',
headers={'Content-type': 'application/json'},
json={'d': {'results' : [{
'Sensor': 'sensor2',
'Date': "/Date(1776417069000)/",
'Value': '29.8d'
}, {
'Sensor': 'sensor4',
'Date': "/Date(1776417578000)/",
'Value': '30.2d'
}],
'__count': '50',
'__next': f"{service.url}/get_best_measurements?$skiptoken=2"
}},
status=200)


result = service.functions.get_best_measurements.count(inline=True).execute()

assert isinstance(result, list)
assert len(result) == 2
assert isinstance(result[0], pyodata.v2.service.EntityProxy)
assert isinstance(result[1], pyodata.v2.service.EntityProxy)

assert hasattr(result, 'total_count'), \
"FunctionImport Collection should preserve __count like get_entities()"
assert result.total_count == 50

assert hasattr(result, 'next_url'), \
"FunctionImport Collection should preserve __next like get_entities()"
assert result.next_url == f"{service.url}/get_best_measurements?$skiptoken=2"

@responses.activate
def test_update_entity(service):
Expand Down
Loading